typesafe-gates 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -30,6 +30,9 @@ could fail (a whole batch silently dropped), the model could soft-refuse (a
30
30
  polite decline parses as nothing), and there was no signal for *how* relevant
31
31
  an item was, so a threshold was prompt wording instead of a number.
32
32
 
33
+ The long version of where these gates came from, with the audit numbers
34
+ behind each threshold, is in [docs/seven-production-gates.md](docs/seven-production-gates.md).
35
+
33
36
  ## Install
34
37
 
35
38
  ```sh
@@ -78,6 +81,17 @@ A gate declares four things:
78
81
  the common shapes: `verdictsOrNull` (fail-open), `verdictOrNull` (one item),
79
82
  `verdictsOr(result, reason => fallback)` (fail-closed).
80
83
 
84
+ The policy covers the judge being unavailable, not bugs in your gate. If
85
+ `state` or `interpret` throws (for example on a malformed webhook payload),
86
+ `run` rejects with that error. A fail-closed caller should catch it and take
87
+ the same conservative path:
88
+
89
+ ```ts
90
+ const decision = await inboundScreenGate
91
+ .runOne(judge, email, inbox)
92
+ .then(inboundDecision, () => ({ action: "hold", reason: "screen_error", alert: true }) as const);
93
+ ```
94
+
81
95
  ## The judge
82
96
 
83
97
  `createJudge(config)` returns a function that asks the same questions of every
@@ -93,10 +107,10 @@ order.
93
107
  - **Time budget.** `budgetMs` aborts everything in flight and returns
94
108
  `over_budget`. Use it on request-path gates (a webhook with a 20 s limit).
95
109
  - **Usage accounting.** `recordUsage` receives one record per invocation with
96
- tokens, request count, estimated cost, task name, model, and `failed`. It is
97
- called even when the invocation failed, because tokens spent before a
98
- failure were billed. A throwing recorder is logged and swallowed: telemetry
99
- never fails a gate.
110
+ tokens, request count, estimated cost, duration, task name, model, and
111
+ `failed`. It is called even when the invocation failed, because tokens
112
+ spent before a failure were billed. It is not awaited, and a throwing
113
+ recorder is logged and swallowed: telemetry never fails or delays a gate.
100
114
  - **No client, no judgment.** With `TYPESAFE_API_KEY` unset the judge returns
101
115
  `no_client`. It never decides what that means for a gate.
102
116
 
@@ -125,7 +139,7 @@ context argument.
125
139
  | `candidateGate(categories, fallback)` | relevance + newsworthiness (Score) + category (Choice) in one request; `rankCandidates` picks the top N | fail-open | as above; filler dropped at P ≥ 0.5 |
126
140
  | `localityGate` | Score, 4 levels: not local / statewide / nearby / hyperlocal; `pickLocalFirst` builds a local-first pool | fail-open | `LOCALITY_MIN_PROBABILITY` (0.5), `LOCALITY_POOL_TARGET` (12, 8 to 30), `SOURCE_LOCALITY_MIN_LEVEL` (0.5) |
127
141
  | `menuPageGate` | yes/no: is this page a restaurant's menu | fail-open | `MENU_MIN_PROBABILITY` (0.4) |
128
- | `inboundScreenGate` | intent (Choice, 7 labels) + AI-directed-instruction injection (yes/no); `decideInboundAction` is the policy table | **fail-closed** | fixed: hold at P(injection) ≥ 0.3, auto-reply at P(sales) ≥ 0.7, silent spam at ≥ 0.8 |
142
+ | `inboundScreenGate` | intent (Choice, 7 labels) + AI-directed-instruction injection (yes/no); `decideInboundAction` is the policy table | **fail-closed** | fixed: hold at P(injection) ≥ 0.3, auto-reply at P(sales) ≥ 0.7, silent spam at ≥ 0.8; mail longer than the screen sees (subject 300, body 6,000 chars) is held |
129
143
  | `factCheckGate` | unsupported + contradicts + personal (three yes/no) per section against the evidence the writer was given | fail-open | `FACT_CHECK_MIN_PROBABILITY` (0.7); contradicts at 0.5; personal note at 0.8 |
130
144
 
131
145
  Provenance, briefly:
@@ -181,15 +195,24 @@ latency.
181
195
  1. Bump `version` in `package.json` and add a `CHANGELOG.md` entry.
182
196
  2. Commit, then publish a GitHub release whose tag is `vX.Y.Z` (a bare
183
197
  `X.Y.Z` also works) targeting `main`. The release workflow checks the tag
184
- against the version, re-runs lint, typecheck, tests and the build, and
185
- publishes to npm with provenance through trusted publishing (no token
186
- stored anywhere). A release saved as a draft creates no tag and runs
187
- nothing.
188
-
189
- The first version of a package cannot be published by trusted publishing,
190
- so 0.1.0 was published by hand with `npm publish --access public` and the
191
- trusted publisher (owner `mpointer`, repository `typesafe-gates`, workflow
192
- `release.yml`) was configured on npmjs.com afterwards.
198
+ against the version and re-runs lint, typecheck, tests and the build. A
199
+ release saved as a draft creates no tag and runs nothing.
200
+ 3. Publish to npm by hand from a clean checkout of the tagged commit, in a
201
+ real terminal:
202
+
203
+ ```sh
204
+ npm publish --access public
205
+ ```
206
+
207
+ npm asks for an MFA step with a passkey; approve it in the browser while
208
+ the command waits. The registry can take a few minutes to show the new
209
+ version.
210
+
211
+ The workflow's own publish step (trusted publishing, owner `mpointer`,
212
+ repository `typesafe-gates`, workflow `release.yml`) fails with
213
+ `403 OIDC permission denied`, because the package requires that passkey
214
+ step and CI cannot complete it. Releases published by hand carry no
215
+ provenance attestation.
193
216
 
194
217
  ## License
195
218
 
package/dist/gate.d.ts CHANGED
@@ -49,7 +49,9 @@ export interface Gate<Input, Context, Q extends Questions, Verdict> {
49
49
  readonly questions: Q;
50
50
  state(input: Input, context: Context): EntryType;
51
51
  interpret(answers: SystemOneResult<Q>["answers"], input: Input, context: Context): Verdict;
52
- /** Judge every input in one all-or-nothing invocation. */
52
+ /** Judge every input in one all-or-nothing invocation. The policy covers
53
+ * the judge being unavailable; an error thrown by `state` or `interpret`
54
+ * rejects instead, so fail-closed callers should catch it and hold. */
53
55
  run(judge: Judge, inputs: readonly Input[], context: Context, opts?: GateRunOptions): Promise<GateResult<Verdict>>;
54
56
  /** Judge a single input. */
55
57
  runOne(judge: Judge, input: Input, context: Context, opts?: GateRunOptions): Promise<GateResult<Verdict>>;
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAErD,MAAM,WAAW,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,SAAS,SAAS,EAAE,OAAO;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,CAAC,CAAC;IACb;;yDAEqD;IACrD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;IACrD,wEAAwE;IACxE,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;CAChG;AAED,MAAM,WAAW,UAAU,CAAC,OAAO;IACjC,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC;AAExE,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,SAAS,SAAS,EAAE,OAAO;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IACjD,SAAS,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;IAC3F,0DAA0D;IAC1D,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACnH,4BAA4B;IAC5B,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;CAC3G;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAC3E,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,GACzC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CA0BlC;AAED;8CAC8C;AAC9C,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAEnE;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAEhE;AAED;;gDAEgD;AAChD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,CAEtG"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAErD,MAAM,WAAW,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,SAAS,SAAS,EAAE,OAAO;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,CAAC,CAAC;IACb;;yDAEqD;IACrD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;IACrD,wEAAwE;IACxE,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;CAChG;AAED,MAAM,WAAW,UAAU,CAAC,OAAO;IACjC,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC;AAExE,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,SAAS,SAAS,EAAE,OAAO;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IACjD,SAAS,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;IAC3F;;2EAEuE;IACvE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACnH,4BAA4B;IAC5B,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;CAC3G;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAC3E,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,GACzC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CA0BlC;AAED;8CAC8C;AAC9C,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAEnE;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAEhE;AAED;;gDAEgD;AAChD,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,CAEtG"}
package/dist/gate.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AA8DA,MAAM,UAAU,UAAU,CACxB,IAA0C;IAE1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;IACpC,MAAM,IAAI,GAAqC;QAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI;QACJ,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;YACzF,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa;gBAAE,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/E,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAU,EAAE,OAAO,CAAC,CAAC;gBAClG,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI;YACtC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACjD,CAAC;KACF,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED;8CAC8C;AAC9C,MAAM,UAAU,cAAc,CAAI,MAAqB;IACrD,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAI,MAAqB;IACpD,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC;AAED;;gDAEgD;AAChD,MAAM,UAAU,UAAU,CAAI,MAAqB,EAAE,QAA4C;IAC/F,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAChF,CAAC"}
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAgEA,MAAM,UAAU,UAAU,CACxB,IAA0C;IAE1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;IACpC,MAAM,IAAI,GAAqC;QAC7C,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI;QACJ,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;YACzF,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa;gBAAE,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/E,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAU,EAAE,OAAO,CAAC,CAAC;gBAClG,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI;YACtC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACjD,CAAC;KACF,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED;8CAC8C;AAC9C,MAAM,UAAU,cAAc,CAAI,MAAqB;IACrD,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAI,MAAqB;IACpD,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC;AAED;;gDAEgD;AAChD,MAAM,UAAU,UAAU,CAAI,MAAqB,EAAE,QAA4C;IAC/F,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAChF,CAAC"}
@@ -53,9 +53,15 @@ export interface Inbox {
53
53
  /** What the inbox is for and what the automatic reply does. */
54
54
  purpose: string;
55
55
  }
56
- /** The body is truncated: the gist and any injected instructions sit near
57
- * the top or are short, and a 50 KB forwarded thread should not be billed
58
- * in full. */
56
+ /** The screen judges at most this much of the subject and body, so a 50 KB
57
+ * forwarded thread is not billed in full. Mail longer than this is held (see
58
+ * `decideInboundAction`): the sender chooses where injected instructions
59
+ * go, and padding past the cut would otherwise hide them from the screen
60
+ * while the responder still reads them. */
61
+ export declare const INBOUND_SUBJECT_MAX_CHARS = 300;
62
+ export declare const INBOUND_BODY_MAX_CHARS = 6000;
63
+ /** True when the screen would not see the whole email. */
64
+ export declare function exceedsScreen(email: Pick<InboundEmail, "subject" | "body">): boolean;
59
65
  export declare function inboundState(email: InboundEmail, inbox: Inbox): {
60
66
  inbox: {
61
67
  name: string;
@@ -71,6 +77,8 @@ export declare function inboundState(email: InboundEmail, inbox: Inbox): {
71
77
  export interface InboundAnswers {
72
78
  intent: Pick<ChoiceResponse<typeof INBOUND_INTENTS>, "choice" | "probabilities">;
73
79
  injection: Pick<NoulResponse, "noul">;
80
+ /** The screen saw only part of the email. */
81
+ truncated?: boolean;
74
82
  }
75
83
  export type InboundDecision = {
76
84
  action: "auto_reply";
@@ -89,7 +97,8 @@ export type InboundDecision = {
89
97
  export declare const INJECTION_HOLD_AT = 0.3;
90
98
  export declare const AUTO_REPLY_MIN = 0.7;
91
99
  export declare const SPAM_SILENT_AT = 0.8;
92
- /** Pure policy. `null` answers mean the screen was unavailable: hold. */
100
+ /** Pure policy. `null` answers mean the screen was unavailable, and
101
+ * `truncated` means it saw only part of the email: hold either way. */
93
102
  export declare function decideInboundAction(answers: InboundAnswers | null): InboundDecision;
94
103
  export declare const inboundScreenGate: import("../gate.js").Gate<InboundEmail, Inbox, {
95
104
  intent: import("@typesafe-ai/sdk").ChoiceQuestion<{
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-screen.d.ts","sourceRoot":"","sources":["../../src/gates/inbound-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAgB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAGzD,eAAO,MAAM,eAAe;;;;;;;;CAalB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,eAAe,CAAC;AAEzD,eAAO,MAAM,iBAAiB;;;;;;;;;;;CAoB7B,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;cAEc;AACd,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;EAU7D;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,EAAE,QAAQ,GAAG,eAAe,CAAC,CAAC;IACjF,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F;;uBAEuB;AACvB,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,eAAe,CAenF;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;kBAM5B,CAAC;AAEH;wDACwD;AACxD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,eAAe,CAEnF"}
1
+ {"version":3,"file":"inbound-screen.d.ts","sourceRoot":"","sources":["../../src/gates/inbound-screen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAgB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAGzD,eAAO,MAAM,eAAe;;;;;;;;CAalB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,eAAe,CAAC;AAEzD,eAAO,MAAM,iBAAiB;;;;;;;;;;;CAoB7B,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;2CAI2C;AAC3C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAE5C,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC,GAAG,OAAO,CAEpF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK;;;;;;;;;;;EAU7D;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,EAAE,QAAQ,GAAG,eAAe,CAAC,CAAC;IACjF,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACtC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F;;uBAEuB;AACvB,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;uEACuE;AACvE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,eAAe,CAgBnF;AAED,eAAO,MAAM,iBAAiB;;;;;;;;;;;kBAU5B,CAAC;AAEH;wDACwD;AACxD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,eAAe,CAEnF"}
@@ -43,17 +43,25 @@ export const INBOUND_QUESTIONS = {
43
43
  false: "The email is written to a human reader only.",
44
44
  }),
45
45
  };
46
- /** The body is truncated: the gist and any injected instructions sit near
47
- * the top or are short, and a 50 KB forwarded thread should not be billed
48
- * in full. */
46
+ /** The screen judges at most this much of the subject and body, so a 50 KB
47
+ * forwarded thread is not billed in full. Mail longer than this is held (see
48
+ * `decideInboundAction`): the sender chooses where injected instructions
49
+ * go, and padding past the cut would otherwise hide them from the screen
50
+ * while the responder still reads them. */
51
+ export const INBOUND_SUBJECT_MAX_CHARS = 300;
52
+ export const INBOUND_BODY_MAX_CHARS = 6_000;
53
+ /** True when the screen would not see the whole email. */
54
+ export function exceedsScreen(email) {
55
+ return email.subject.length > INBOUND_SUBJECT_MAX_CHARS || email.body.length > INBOUND_BODY_MAX_CHARS;
56
+ }
49
57
  export function inboundState(email, inbox) {
50
58
  return {
51
59
  inbox: { name: inbox.name, purpose: inbox.purpose },
52
60
  email: {
53
61
  from: `${email.fromName} <${email.fromEmail}>`,
54
62
  senderHistory: email.knownSender ? "has emailed this inbox before" : "first contact",
55
- subject: email.subject.slice(0, 300),
56
- body: email.body.slice(0, 6_000),
63
+ subject: email.subject.slice(0, INBOUND_SUBJECT_MAX_CHARS),
64
+ body: email.body.slice(0, INBOUND_BODY_MAX_CHARS),
57
65
  },
58
66
  };
59
67
  }
@@ -63,10 +71,13 @@ export function inboundState(email, inbox) {
63
71
  export const INJECTION_HOLD_AT = 0.3;
64
72
  export const AUTO_REPLY_MIN = 0.7;
65
73
  export const SPAM_SILENT_AT = 0.8;
66
- /** Pure policy. `null` answers mean the screen was unavailable: hold. */
74
+ /** Pure policy. `null` answers mean the screen was unavailable, and
75
+ * `truncated` means it saw only part of the email: hold either way. */
67
76
  export function decideInboundAction(answers) {
68
77
  if (!answers)
69
78
  return { action: "hold", reason: "screen_unavailable", alert: true };
79
+ if (answers.truncated)
80
+ return { action: "hold", reason: "too_long_to_screen", alert: true };
70
81
  const pSales = probabilityOf(answers.intent, "ad_inquiry", "negotiation");
71
82
  const pSpam = probabilityOf(answers.intent, "spam_phishing");
72
83
  const pInjection = answers.injection.noul;
@@ -86,7 +97,11 @@ export const inboundScreenGate = defineGate({
86
97
  policy: "fail-closed",
87
98
  questions: INBOUND_QUESTIONS,
88
99
  state: (email, inbox) => inboundState(email, inbox),
89
- interpret: (answers) => ({ intent: answers.intent, injection: answers.injection }),
100
+ interpret: (answers, email) => ({
101
+ intent: answers.intent,
102
+ injection: answers.injection,
103
+ truncated: exceedsScreen(email),
104
+ }),
90
105
  });
91
106
  /** The full fail-closed path: a result from `inboundScreenGate.runOne` to a
92
107
  * decision, holding when the screen was unavailable. */
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-screen.js","sourceRoot":"","sources":["../../src/gates/inbound-screen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbound-screen.js","sourceRoot":"","sources":["../../src/gates/inbound-screen.ts"],"names":[],"mappings":"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"}
@@ -63,7 +63,9 @@ export interface LocalFirstPick<T> {
63
63
  }
64
64
  /** The local-first pool: every item at P(nearby or closer) >= threshold,
65
65
  * topped up to `target` with the next-most-local items that are at least
66
- * statewide-ish. A slow news day yields a smaller pool, not an off-topic one. */
66
+ * statewide-ish. A slow news day yields a smaller pool, not an off-topic one.
67
+ * `verdicts[i]` must be the verdict for `items[i]`; when the gate was
68
+ * unavailable, skip this and keep the previous path instead. */
67
69
  export declare function pickLocalFirst<T>(items: readonly T[], verdicts: readonly LocalityVerdict[], opts?: {
68
70
  target?: number;
69
71
  minProbability?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"locality.d.ts","sourceRoot":"","sources":["../../src/gates/locality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAS,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAK7D,eAAO,MAAM,eAAe,kqBAKlB,CAAC;AAEX,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,eAAO,MAAM,iBAAiB,4sBAS7B,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS;;;;;;;;;;;EAKrE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,eAAe,CAE1F;AAED,eAAO,MAAM,YAAY;;mBAMvB,CAAC;AAEH,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD;kDACkD;AAClD,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C;mDACmD;AACnD,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAEpG;AAED,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAK7F;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,gCAAgC;IAChC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;iFAEiF;AACjF,wBAAgB,cAAc,CAAC,CAAC,EAC9B,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,QAAQ,EAAE,SAAS,eAAe,EAAE,EACpC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,cAAc,CAAC,CAAC,CAAC,CAcnB;AAWD,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAKpG;AAED;kEACkE;AAClE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,EAAE,UAAU,SAAI,GAAG,MAAM,GAAG,IAAI,CAGhG"}
1
+ {"version":3,"file":"locality.d.ts","sourceRoot":"","sources":["../../src/gates/locality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAS,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAK7D,eAAO,MAAM,eAAe,kqBAKlB,CAAC;AAEX,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,eAAO,MAAM,iBAAiB,4sBAS7B,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS;;;;;;;;;;;EAKrE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,eAAe,CAE1F;AAED,eAAO,MAAM,YAAY;;mBAMvB,CAAC;AAEH,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD;kDACkD;AAClD,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C;mDACmD;AACnD,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAEpG;AAED,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAK7F;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,gCAAgC;IAChC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;gEAIgE;AAChE,wBAAgB,cAAc,CAAC,CAAC,EAC9B,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,QAAQ,EAAE,SAAS,eAAe,EAAE,EACpC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,cAAc,CAAC,CAAC,CAAC,CAiBnB;AAWD,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAKpG;AAED;kEACkE;AAClE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,EAAE,UAAU,SAAI,GAAG,MAAM,GAAG,IAAI,CAGhG"}
@@ -59,8 +59,13 @@ export function localPoolTarget(env = process.env) {
59
59
  }
60
60
  /** The local-first pool: every item at P(nearby or closer) >= threshold,
61
61
  * topped up to `target` with the next-most-local items that are at least
62
- * statewide-ish. A slow news day yields a smaller pool, not an off-topic one. */
62
+ * statewide-ish. A slow news day yields a smaller pool, not an off-topic one.
63
+ * `verdicts[i]` must be the verdict for `items[i]`; when the gate was
64
+ * unavailable, skip this and keep the previous path instead. */
63
65
  export function pickLocalFirst(items, verdicts, opts = {}) {
66
+ if (verdicts.length !== items.length) {
67
+ throw new RangeError(`pickLocalFirst: ${items.length} items but ${verdicts.length} verdicts`);
68
+ }
64
69
  const target = opts.target ?? localPoolTarget();
65
70
  const threshold = opts.minProbability ?? localityMinProbability();
66
71
  const local = new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"locality.js","sourceRoot":"","sources":["../../src/gates/locality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,EAAsB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEzE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,iKAAiK;IACjK,mMAAmM;IACnM,kLAAkL;IAClL,yHAAyH;CACjH,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CACpC;IACE,QAAQ,EAAE,yFAAyF;IACnG,QAAQ,EAAE;QACR,oLAAoL;QACpL,gIAAgI;KACjI;CACF,EACD,eAAe,CAChB,CAAC;AAsBF,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,MAAM,UAAU,aAAa,CAAC,IAAkB,EAAE,SAAoB;IACpE,OAAO;QACL,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,MAAM,EAAE;QACnG,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE;KAC9G,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAyC;IACvE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,CAAC;AACzF,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;IACrC,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,WAAW;IACnB,SAAS,EAAE,EAAE,QAAQ,EAAE,iBAAiB,EAAE;IAC1C,KAAK,EAAE,CAAC,IAAkB,EAAE,SAAoB,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC;IACnF,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;CAC1D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AACpD;kDACkD;AAClD,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAC5C;mDACmD;AACnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,MAAM,UAAU,sBAAsB,CAAC,MAA0C,OAAO,CAAC,GAAG;IAC1F,OAAO,oBAAoB,CAAC,0BAA0B,EAAE,gCAAgC,EAAE,GAAG,CAAC,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAA0C,OAAO,CAAC,GAAG;IACnF,OAAO,gBAAgB,CACrB,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,yBAAyB,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EACpG,GAAG,CACJ,CAAC;AACJ,CAAC;AASD;;iFAEiF;AACjF,MAAM,UAAU,cAAc,CAC5B,KAAmB,EACnB,QAAoC,EACpC,OAAqD,EAAE;IAEvD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,IAAI,sBAAsB,EAAE,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,KAAK;SACjB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,IAAI,gBAAgB,CAAC;SAChF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAC;SAC3E,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;IAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AAClG,CAAC;AAED,iFAAiF;AACjF,2EAA2E;AAC3E,2EAA2E;AAC3E,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,wEAAwE;AACxE,qBAAqB;AAErB,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,MAAM,UAAU,sBAAsB,CAAC,MAA0C,OAAO,CAAC,GAAG;IAC1F,OAAO,gBAAgB,CACrB,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,iCAAiC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EACjG,GAAG,CACJ,CAAC;AACJ,CAAC;AAED;kEACkE;AAClE,MAAM,UAAU,YAAY,CAAC,QAAoC,EAAE,UAAU,GAAG,CAAC;IAC/E,IAAI,QAAQ,CAAC,MAAM,GAAG,UAAU;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"locality.js","sourceRoot":"","sources":["../../src/gates/locality.ts"],"names":[],"mappings":"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"}
package/dist/judge.d.ts CHANGED
@@ -23,12 +23,12 @@ export interface JudgeConfig {
23
23
  * reason `no_client`. Omit to build one from `TYPESAFE_API_KEY` lazily. */
24
24
  client?: SystemOneClient | null;
25
25
  /** Receives one record per invocation, including failed ones (tokens spent
26
- * before a failure were still billed). Errors thrown here are swallowed:
27
- * telemetry must never fail a gate. */
26
+ * before a failure were still billed). It is not awaited, and errors
27
+ * thrown here are swallowed: telemetry must never fail or delay a gate. */
28
28
  recordUsage?: UsageRecorder;
29
29
  /** Where "falling back" warnings go. `null` silences them. Default: console. */
30
30
  logger?: Pick<Console, "warn"> | null;
31
- /** Requests in flight per invocation. Default 8. */
31
+ /** Requests in flight per invocation, a positive integer. Default 8. */
32
32
  concurrency?: number;
33
33
  rates?: Rates;
34
34
  /** Model override for every request. */
@@ -1 +1 @@
1
- {"version":3,"file":"judge.d.ts","sourceRoot":"","sources":["../src/judge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAA8B,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1G,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,GAAG,aAAa,GAAG,SAAS,CAAC;AAE3F;qEACqE;AACrE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B;+EAC2E;IAC3E,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC;;2CAEuC;IACvC,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,gFAAgF;IAChF,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACtC,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B;6EACyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,0EAA0E;AAC1E,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,EAC5C,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,SAAS,EAAE,CAAC,EACZ,IAAI,EAAE,YAAY,KACf,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAIzD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,GAAE,WAAgB,GAAG,KAAK,CA4G3D"}
1
+ {"version":3,"file":"judge.d.ts","sourceRoot":"","sources":["../src/judge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAA8B,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1G,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,GAAG,aAAa,GAAG,SAAS,CAAC;AAE3F;qEACqE;AACrE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B;+EAC2E;IAC3E,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAChC;;+EAE2E;IAC3E,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,gFAAgF;IAChF,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACtC,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B;6EACyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,0EAA0E;AAC1E,MAAM,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,EAC5C,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,SAAS,EAAE,CAAC,EACZ,IAAI,EAAE,YAAY,KACf,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AAIzD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,GAAE,WAAgB,GAAG,KAAK,CAoH3D"}
package/dist/judge.js CHANGED
@@ -28,6 +28,11 @@ export function createJudge(config = {}) {
28
28
  const client = await getClient();
29
29
  const usage = { inputTokens: 0, outputTokens: 0, requests: 0 };
30
30
  const requestedModel = opts.model ?? config.model ?? "";
31
+ const concurrency = opts.concurrency ?? config.concurrency ?? DEFAULT_CONCURRENCY;
32
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
33
+ throw new RangeError(`[typesafe-gates:${opts.task}] concurrency must be a positive integer, got ${concurrency}`);
34
+ }
35
+ const started = performance.now();
31
36
  async function record(model, failed) {
32
37
  if (!config.recordUsage)
33
38
  return;
@@ -39,6 +44,7 @@ export function createJudge(config = {}) {
39
44
  model: model || requestedModel || "jev-latest",
40
45
  provider: "typesafe",
41
46
  failed,
47
+ durationMs: performance.now() - started,
42
48
  estimatedCostUsd: estimateCostUsd(usage, rates),
43
49
  meta: opts.meta ?? {},
44
50
  ...usage,
@@ -78,8 +84,9 @@ export function createJudge(config = {}) {
78
84
  : { state: states[i], questions };
79
85
  const res = await client.systemOne(request, { signal: abort.signal });
80
86
  answers[i] = res.answers;
81
- usage.inputTokens += res.usage.input_tokens;
82
- usage.outputTokens += res.usage.output_tokens;
87
+ // SDK >= 0.7 reports null when the API omits a count.
88
+ usage.inputTokens += res.usage.input_tokens ?? 0;
89
+ usage.outputTokens += res.usage.output_tokens ?? 0;
83
90
  usage.requests += 1;
84
91
  model ||= res.model;
85
92
  }
@@ -92,7 +99,7 @@ export function createJudge(config = {}) {
92
99
  }
93
100
  }
94
101
  }
95
- const width = Math.min(opts.concurrency ?? config.concurrency ?? DEFAULT_CONCURRENCY, states.length);
102
+ const width = Math.min(concurrency, states.length);
96
103
  try {
97
104
  await Promise.all(Array.from({ length: width }, () => worker()));
98
105
  }
@@ -102,7 +109,8 @@ export function createJudge(config = {}) {
102
109
  opts.signal?.removeEventListener("abort", onOuterAbort);
103
110
  }
104
111
  const incomplete = failed || abort.signal.aborted || usage.requests < states.length;
105
- await record(model, incomplete);
112
+ // Not awaited: a slow or hung recorder must never hold up the gate.
113
+ void record(model, incomplete);
106
114
  if (incomplete) {
107
115
  const reason = budgetHit
108
116
  ? "over_budget"
package/dist/judge.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"judge.js","sourceRoot":"","sources":["../src/judge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"judge.js","sourceRoot":"","sources":["../src/judge.ts"],"names":[],"mappings":"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"}
package/dist/usage.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface UsageRecord extends UsageTotals {
15
15
  provider: "typesafe";
16
16
  /** True when the invocation ended unavailable; the tokens were still billed. */
17
17
  failed: boolean;
18
+ /** Wall-clock time of the invocation, from first request to result. */
19
+ durationMs: number;
18
20
  estimatedCostUsd: number;
19
21
  /** Whatever the caller attached: tenant id, run id, request id. */
20
22
  meta: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;oDACgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,UAAU,CAAC;IACrB,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1E;wEACwE;AACxE,eAAO,MAAM,SAAS;;;CAAiE,CAAC;AAExF,MAAM,WAAW,KAAK;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,GAAE,KAAiB,GAAG,MAAM,CAKpF"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;oDACgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,UAAU,CAAC;IACrB,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1E;wEACwE;AACxE,eAAO,MAAM,SAAS;;;CAAiE,CAAC;AAExF,MAAM,WAAW,KAAK;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,GAAE,KAAiB,GAAG,MAAM,CAKpF"}
package/dist/usage.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAyBA;wEACwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,kBAAkB,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAW,CAAC;AAOxF,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,QAAe,SAAS;IAC1E,OAAO,CACL,CAAC,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,mBAAmB,CAAC;QAC/F,SAAS,CACV,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AA2BA;wEACwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,kBAAkB,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAW,CAAC;AAOxF,MAAM,UAAU,eAAe,CAAC,KAAkB,EAAE,QAAe,SAAS;IAC1E,OAAO,CACL,CAAC,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,mBAAmB,CAAC;QAC/F,SAAS,CACV,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typesafe-gates",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Probabilistic decision gates for LLM pipelines on TypeSafe AI's System One (Jev): typed Score, Choice and yes/no judgments with explicit thresholds, a stated fail-open or fail-closed policy per gate, all-or-nothing batches, time budgets, and usage accounting. Ships six reference gates from production.",
5
5
  "license": "MIT",
6
6
  "author": "Mike Pointer <mpointer@gmail.com>",
@@ -39,7 +39,8 @@
39
39
  "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.test.json",
40
40
  "test": "vitest run",
41
41
  "lint": "eslint .",
42
- "prepublishOnly": "npm run typecheck && npm run test && npm run build"
42
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build",
43
+ "coverage": "vitest run --coverage"
43
44
  },
44
45
  "keywords": [
45
46
  "typesafe-ai",
@@ -60,12 +61,13 @@
60
61
  "devDependencies": {
61
62
  "@types/node": "^22.0.0",
62
63
  "@typesafe-ai/sdk": "0.6.0",
63
- "typescript": "^5.7.0",
64
- "typescript-eslint": "^8.0.0",
65
- "eslint": "^9.0.0",
66
- "vitest": "^3.0.0"
64
+ "@vitest/coverage-v8": "^5.0.5",
65
+ "eslint": "^10.13.0",
66
+ "typescript": "~6.0.3",
67
+ "typescript-eslint": "^8.72.1",
68
+ "vitest": "^5.0.5"
67
69
  },
68
70
  "engines": {
69
- "node": ">=20"
71
+ "node": ">=22.12"
70
72
  }
71
73
  }