bare-agent 0.46.2 → 0.46.3

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.
@@ -256,7 +256,9 @@ const { answers, costUsd, raw } = await jev.classify('I was charged twice, pleas
256
256
 
257
257
  Three question types, kept verbatim from Jev's own contract: `noul` (binary probability `0..1`), `choice` (pick one of `criteria`'s keys, plus untrusted `probabilities`/`confidence`), `score` (a position `0..N-1` on `criteria`'s legend). Jev's reply is **untrusted model output** — `classify()` schema-checks the discriminator field against the question that asked it (`ValidationError`, stamped `lib:'bare-agent'`, on a type mismatch or an out-of-range value) before returning it; `probabilities`/`confidence` pass through unvalidated. A question's `instructions` can be a string, or the documented TypeSafe structured forms — an array or a plain object with named parts (e.g. `question`/`inspect`/`focus`/`ignore`) — for giving a question multiple named parts; empty `{}`/`[]`/non-plain values are rejected as missing instructions.
258
258
 
259
- **Injection hardening is on by default, and shape-aware.** `state` is untrusted and can carry an embedded attack (`"you are now…"`, `"ignore previous instructions"`); `classify()` wraps every question's `instructions` with a defensive preamble — prefixed onto a string, prepended as element 0 of an array, or added under a reserved `__hardening__` key on an object — so the classifier treats `state` as data, not commands (copy-on-write, never mutates your `questions` object; a caller-supplied `__hardening__` key is rejected so it can't overwrite the preamble). Opt out with `harden: false` on the constructor or per call.
259
+ **Injection hardening is on by default, and shape-aware.** `state` is untrusted and can carry an embedded attack (`"you are now…"`, `"ignore previous instructions"`); `classify()` wraps every question's `instructions` with a defensive preamble — prefixed onto a string, prepended as element 0 of an array, or added under a reserved `__hardening__` key on an object — so the classifier treats `state` as data, not commands (copy-on-write, never mutates your `questions` object; a caller-supplied `__hardening__` key is rejected so it can't overwrite the preamble). Opt out with `harden: false` on the constructor or per call. **The preamble is part of the question, so hardened and unhardened calls can return different values for the same `state`** — the shift can be large enough to move a decision cutoff. Tune any decision threshold with hardening in the *same* state you ship it in — a threshold tuned against `harden:false` or the raw API silently moves once hardening is on.
260
+
261
+ **Jev answers are calibrated, not deterministic.** Repeated identical `classify()` calls can differ slightly, and a value sitting on a threshold can cross it between runs — this is the model, not the provider. In tests and thresholds, assert on ranges or on which side of a cutoff a value falls, never on an exact value.
260
262
 
261
263
  **Calibrate a tier before you trust it.** `calibrateJev` (exported from `bare-agent`, mirrors `calibrate`/judge's harness) grades a Jev model against a frozen clear-case battery (`noul`/`choice`/`score` + a false-positive trap) **and** a multi-style injection battery, admitting only if the clear-case floor clears with zero reds **and** every injection style is resisted:
262
264
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-agent",
3
- "version": "0.46.2",
3
+ "version": "0.46.3",
4
4
  "files": [
5
5
  "index.js",
6
6
  "index.d.ts",
@@ -21,7 +21,7 @@ export class JevProvider {
21
21
  * @param {number} [options.deadlineMs] - Total call-duration deadline (ms); 0 disables (default).
22
22
  * @param {{in: number, out: number, cacheReadMult?: number, cacheWriteMult?: number}} [options.rates] - Per-1K-token USD rates for authoritative pricing (Jev: `{ in: 0.042/1000, out: 0 }`).
23
23
  * @param {boolean} [options.exposeErrorBody=false] - Include the raw error body on a ProviderError (default off).
24
- * @param {boolean} [options.harden=true] - Wrap each question's instructions with a defensive preamble (prefixed for a string, prepended as element 0 for an array, or added under a reserved key for an object), treating `state` as untrusted data and resisting embedded role/label-override attempts. Overridable per-call via `opts.harden`.
24
+ * @param {boolean} [options.harden=true] - Wrap each question's instructions with a defensive preamble (prefixed for a string, prepended as element 0 for an array, or added under a reserved key for an object), treating `state` as untrusted data and resisting embedded role/label-override attempts. Overridable per-call via `opts.harden`. NOTE: the preamble is part of the question, so hardened and unhardened calls can return DIFFERENT values for the same `state` — the shift can be large enough to move a decision cutoff. Tune any decision threshold with hardening in the SAME state you ship it in — a cutoff tuned against `harden:false` or the raw API will be off once hardening is on.
25
25
  */
26
26
  constructor(options?: {
27
27
  apiKey?: string | undefined;
@@ -53,6 +53,10 @@ export class JevProvider {
53
53
  harden: boolean;
54
54
  /**
55
55
  * Classify `state` against one or more typed `questions`. See the class doc for the primitive tags.
56
+ *
57
+ * Jev answers are CALIBRATED, not DETERMINISTIC — repeated identical calls can differ slightly,
58
+ * and a value sitting on a threshold can cross it between runs. Assert on RANGES or on the side of
59
+ * a threshold, never on an exact value (`toBe(0.62)` will flake).
56
60
  * @param {string|object|any[]} state - The shared input all questions judge (Jev's `state`).
57
61
  * @param {Record<string, {type: 'noul'|'choice'|'score', instructions: string|object|any[], criteria?: any}>} questions - Keyed questions; each judged independently against `state`.
58
62
  * @param {object} [opts]
@@ -77,7 +77,7 @@ class JevProvider {
77
77
  * @param {number} [options.deadlineMs] - Total call-duration deadline (ms); 0 disables (default).
78
78
  * @param {{in: number, out: number, cacheReadMult?: number, cacheWriteMult?: number}} [options.rates] - Per-1K-token USD rates for authoritative pricing (Jev: `{ in: 0.042/1000, out: 0 }`).
79
79
  * @param {boolean} [options.exposeErrorBody=false] - Include the raw error body on a ProviderError (default off).
80
- * @param {boolean} [options.harden=true] - Wrap each question's instructions with a defensive preamble (prefixed for a string, prepended as element 0 for an array, or added under a reserved key for an object), treating `state` as untrusted data and resisting embedded role/label-override attempts. Overridable per-call via `opts.harden`.
80
+ * @param {boolean} [options.harden=true] - Wrap each question's instructions with a defensive preamble (prefixed for a string, prepended as element 0 for an array, or added under a reserved key for an object), treating `state` as untrusted data and resisting embedded role/label-override attempts. Overridable per-call via `opts.harden`. NOTE: the preamble is part of the question, so hardened and unhardened calls can return DIFFERENT values for the same `state` — the shift can be large enough to move a decision cutoff. Tune any decision threshold with hardening in the SAME state you ship it in — a cutoff tuned against `harden:false` or the raw API will be off once hardening is on.
81
81
  */
82
82
  constructor(options = {}) {
83
83
  this.apiKey = options.apiKey;
@@ -92,6 +92,10 @@ class JevProvider {
92
92
 
93
93
  /**
94
94
  * Classify `state` against one or more typed `questions`. See the class doc for the primitive tags.
95
+ *
96
+ * Jev answers are CALIBRATED, not DETERMINISTIC — repeated identical calls can differ slightly,
97
+ * and a value sitting on a threshold can cross it between runs. Assert on RANGES or on the side of
98
+ * a threshold, never on an exact value (`toBe(0.62)` will flake).
95
99
  * @param {string|object|any[]} state - The shared input all questions judge (Jev's `state`).
96
100
  * @param {Record<string, {type: 'noul'|'choice'|'score', instructions: string|object|any[], criteria?: any}>} questions - Keyed questions; each judged independently against `state`.
97
101
  * @param {object} [opts]