@fayegram-ai/decision-bridge 0.1.1 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Notable user-facing changes by version.
4
4
 
5
+ ## 0.1.2
6
+
7
+ - Add opt-in Jev images, typed bounding boxes, native JSON assets, and long context
8
+ across SDK and CLI, with capability preflight and nullable token usage.
9
+
5
10
  ## 0.1.1
6
11
 
7
12
  - Add OpenAI `default`/`luna` selectors and requested/resolved model metadata; improve CLI configuration and documentation.
package/README.md CHANGED
@@ -29,7 +29,7 @@ embedded WAV/MP3 audio and MP4/WebM video, ordered images → audio → videos
29
29
  The common API checks provider/model capabilities before dispatch; refusals are
30
30
  errors.
31
31
 
32
- **Version 0.1.1 · Node.js 22.16+ or modern browsers (SDK) · ESM · zero runtime dependencies**
32
+ **Version 0.1.2 · Node.js 22.16+ or modern browsers (SDK) · ESM · zero runtime dependencies**
33
33
 
34
34
  The npm package is
35
35
  [@fayegram-ai/decision-bridge](https://www.npmjs.com/package/@fayegram-ai/decision-bridge).
@@ -53,7 +53,7 @@ Use these model selectors with the built-in decision providers:
53
53
  | Cloudflare (`cloudflare-decisions`) | `clef-flash`, `@cf/cloudflare/clef-flash` | — |
54
54
  | Cloudflare (`cloudflare-decisions`) | `clef-omni`, `@cf/cloudflare/clef-omni` | — |
55
55
 
56
- `jev-latest` is Jev's default alias. Jev also accepts explicit model IDs available through your TypeSafe account;
56
+ `jev-latest` is Jev's default alias. Jev also accepts `jev-preview` and explicit model IDs available through your TypeSafe account;
57
57
  use `bridge.listModels()` or the CLI `models` command to inspect that catalog.
58
58
  The bridge does not maintain a static list of Jev versions. A moving alias or a
59
59
  catalog entry does not guarantee inference access or reproducible model quality.
@@ -80,7 +80,7 @@ All native providers support shared state and the common `choice`, `noul`, and
80
80
 
81
81
  | Provider | Additional supported features | Guide |
82
82
  | --- | --- | --- |
83
- | Jev | Native decisions through the TypeSafe API | [Provider reference](docs/reference/PROVIDERS.md#native-jev) |
83
+ | Jev | Native decisions, opt-in images, boxes, and long context through TypeSafe | [Jev guide](docs/guides/JEV.md) |
84
84
  | OpenAI Decisions | Ordered text/images, typed choices, labelled scores, safety identifiers, native access | [OpenAI Decisions](docs/guides/OPENAI_DECISIONS.md) |
85
85
  | Cloudflare Clef / Clef-flash | Embedded images before text, typed choices, labelled scores, native access, Workers AI binding, optional Gateway | [Cloudflare decisions](docs/guides/CLOUDFLARE_DECISIONS.md) |
86
86
  | Cloudflare Clef-omni | The same decision and transport features, plus embedded audio/video | [Omni media and limits](docs/guides/CLOUDFLARE_DECISIONS.md#omni-audio-and-video) |
package/docs/README.md CHANGED
@@ -17,7 +17,7 @@ validated, structured decisions.
17
17
  For provider-specific input forms, consult the
18
18
  [capability comparison](reference/PROVIDERS.md#common-request-capabilities).
19
19
  The common bridge API and dedicated native APIs have separate request shapes;
20
- the OpenAI and Cloudflare guides cover both.
20
+ the Jev, OpenAI and Cloudflare guides cover both.
21
21
 
22
22
  ## Use the bridge
23
23
 
@@ -27,6 +27,7 @@ the OpenAI and Cloudflare guides cover both.
27
27
  | [Browser integration](guides/BROWSER.md) | Direct browser use, CORS requirements, and credential exposure |
28
28
  | [Platforms](guides/PLATFORMS.md) | Windows, macOS, and Linux setup |
29
29
  | [Switching models](guides/SWITCHING_MODELS.md) | Backend and model selection |
30
+ | [Jev](guides/JEV.md) | Common/native images, boxes, experimental long context, model selection, and CLI opt-ins |
30
31
  | [OpenAI Decisions](guides/OPENAI_DECISIONS.md) | Model selectors and upstream targets, selection metadata, common/native access, ordered images, and typed values |
31
32
  | [Cloudflare Clef / Clef-flash / Clef-omni](guides/CLOUDFLARE_DECISIONS.md) | Common/native decisions, embedded images and Omni audio/video, REST and Workers AI binding, busy-mode compatibility, and optional Gateway |
32
33
  | [Customization](guides/CUSTOMIZATION.md) | Questions, templates, extensions, and policies |
@@ -38,7 +38,7 @@ Application / CLI
38
38
  application policy / storage / downstream action
39
39
  ```
40
40
 
41
- Native OpenAI and Cloudflare providers also expose `createDecision` directly.
41
+ Native Jev, OpenAI and Cloudflare providers also expose `createDecision` directly.
42
42
  Common and native paths share provider validation, codecs, and bounded transport.
43
43
  Direct native calls use provider deadlines; bridge calls additionally enforce the
44
44
  shared evaluation deadline and extension lifecycle.
@@ -82,6 +82,7 @@ See [OpenAI model selection](../guides/OPENAI_DECISIONS.md#model-selection).
82
82
  | `schemas/decision.config.schema.json` | Shipped CLI configuration structure and editor support |
83
83
  | `src/providers/` | API orchestration, model profiles, preflight, codecs, bounded image/audio/video inspection, and transports |
84
84
  | `src/providers/openai-profile.mjs`, `src/providers/decisions-preflight.mjs` | Release-owned OpenAI selection, capabilities, and preparation of selected and outgoing requests |
85
+ | `src/providers/jev-profile.mjs`, `src/providers/jev-preflight.mjs`, `src/providers/jev-images.mjs` | Jev experimental policy, common/native assets, and image preflight |
85
86
  | `src/embedded.mjs` | Shared embedded data URL and canonical base64 checks |
86
87
  | `src/transport.mjs`, `src/response-transport.mjs` | Credential destinations, bounded bodies, safe errors, retries |
87
88
  | `src/async.mjs` | Deadlines and cancellation races |
@@ -102,7 +103,8 @@ claiming equivalent statistical semantics across models.
102
103
 
103
104
  The bridge takes result-validation policy from the provider contract. Cloudflare
104
105
  uses an allowance for four-decimal distributions; other built-ins retain the
105
- baseline. Response metadata cannot relax validation. See the [SDK reference](../reference/SDK.md).
106
+ baseline. Jev additionally permits explicitly unknown token counts while requiring
107
+ native confidence. Response metadata cannot relax validation. See the [SDK reference](../reference/SDK.md).
106
108
  Application code passes that precision explicitly when using `decideChoice`; the
107
109
  policy helper does not infer it from the answer or its metadata.
108
110
 
@@ -35,6 +35,9 @@ headers used by the SDK. For Jev evaluation those include `POST`, `Authorization
35
35
  and `Content-Type`. If the preflight is rejected, the browser sends no evaluation
36
36
  request; neither the SDK nor a browser setting can override that server policy.
37
37
  Check your intended origin with TypeSafe before relying on direct Jev calls.
38
+ When Jev experimental features are enabled, CORS must also permit the selected
39
+ `TypeSafe-Danger-Zone-Images` and `TypeSafe-Danger-Zone-Long-Context` headers.
40
+ The [Jev guide](JEV.md) covers browser-safe embedded assets and boxes.
38
41
 
39
42
  Any key delivered to browser JavaScript can be read by the app's users. Only use
40
43
  a credential that the provider explicitly permits for browser exposure. If an
@@ -25,7 +25,7 @@ You can also install a verified local archive, replacing the path below. See
25
25
  a source checkout:
26
26
 
27
27
  ```bash
28
- npm install /path/to/fayegram-ai-decision-bridge-0.1.1.tgz
28
+ npm install /path/to/fayegram-ai-decision-bridge-0.1.2.tgz
29
29
  ```
30
30
 
31
31
  The installed package contains the SDK, declarations, command runner, examples,
@@ -0,0 +1,174 @@
1
+ # Jev images, boxes, and long context
2
+
3
+ Jev uses TypeSafe's native `POST /v1/systemone` endpoint. The SDK defaults to
4
+ `jev-latest`; `jev-preview` and explicit version IDs are also accepted. Model
5
+ listing is account-specific and may list aliases without versioned IDs.
6
+ The returned `model` records the service-reported version independently of
7
+ `meta.requestedModel`.
8
+
9
+ ## Experimental opt-ins
10
+
11
+ Jev images and long context are disabled by default. Enable each independently
12
+ in provider configuration. TypeSafe treats these capabilities as experimental;
13
+ their accuracy, formats, and limits can change.
14
+
15
+ ```javascript
16
+ import { createBridge, JevProvider, box, noul } from '@fayegram-ai/decision-bridge';
17
+
18
+ const provider = new JevProvider({
19
+ model: 'jev-1.14.0',
20
+ dangerZone: { images: true, longContext: true },
21
+ });
22
+ const bridge = createBridge({ provider });
23
+
24
+ // imageBase64 is a caller-prepared PNG or JPEG, without a data URL prefix.
25
+ const result = await bridge.evaluate({
26
+ input: [
27
+ { type: 'text', text: 'A photograph from the kitchen camera.' },
28
+ { type: 'image', dataUrl: 'data:image/png;base64,' + imageBase64 },
29
+ ],
30
+ questions: {
31
+ present: noul('Is there a coffee mug in the image?'),
32
+ mug: box('The coffee mug in the image'),
33
+ },
34
+ });
35
+ console.log(result.answers.present.noul, result.answers.mug.box);
36
+ ```
37
+
38
+ `dangerZone.images` sends `TypeSafe-Danger-Zone-Images: true` and enables common
39
+ image input and boxes. It works with Jev 1.13 and 1.14 for images. Boxes require
40
+ `jev-preview` or a pinned Jev version of 1.14.0 or newer. Local box capability
41
+ lookup remains conservative for moving stable aliases and unknown model names;
42
+ use a supported pinned ID or `jev-preview` for boxes. Account access and hosted
43
+ model availability are still checked by the service.
44
+
45
+ `dangerZone.longContext` sends `TypeSafe-Danger-Zone-Long-Context: true`. TypeSafe
46
+ documents 262k tokens for both the per-request and per-question contexts with
47
+ this header, compared with default limits of 64k and 32k respectively. This
48
+ option works on Jev 1.13 and 1.14. It does not change the byte budget or estimate
49
+ token counts. Longer contexts are slower and less accurate.
50
+
51
+ Ordered common input becomes a state array in the same order. Text stays text;
52
+ each image becomes an asset reference. The SDK chooses references that do not
53
+ collide with the input or question text. Jev does not support image detail
54
+ controls: omit `detail`, or use `null` or `auto`. Audio/video, typed choices,
55
+ labelled scores, and safety identifiers remain unsupported by this provider.
56
+
57
+ ## Native assets in instructions and criteria
58
+
59
+ Use `provider.createDecision()` when images belong inside structured state,
60
+ instructions, or criteria. It accepts the native JSON request shape and returns
61
+ `{ decision, meta }`. Native calls share validation, transport, cancellation,
62
+ and retry policy with common calls, and bypass bridge extensions and observers.
63
+
64
+ ```javascript
65
+ const { decision, meta } = await provider.createDecision({
66
+ state: { photo: 'photo-unique-reference' },
67
+ assets: {
68
+ 'photo-unique-reference': {
69
+ type: 'image', format: 'png', data_base64: imageBase64,
70
+ },
71
+ 'example-unique-reference': {
72
+ type: 'image', format: 'jpg', data_base64: exampleBase64,
73
+ },
74
+ },
75
+ questions: {
76
+ same: noul('Does the object in `photo` match this picture: example-unique-reference?'),
77
+ target: box('The matching object in `photo`'),
78
+ },
79
+ });
80
+ console.log(decision.answers.target.box, meta.requestedModel);
81
+ ```
82
+
83
+ References are substituted in string values, including inside sentences and
84
+ nested JSON. Object keys are not substituted. Every asset must be referenced.
85
+ Whole-value references are resolved first. In longer strings, matches are
86
+ consumed from left to right; when keys overlap at the same position, the current
87
+ API uses asset-map order. A reference hidden by another match does not count as
88
+ used. Preflight rejects unused assets before dispatch. Place references in
89
+ separate values when both images are intended.
90
+ Use long unique references (for example, fresh UUIDs) that cannot accidentally
91
+ match ordinary text. Each reference is at most 1024 UTF-8 bytes; all reference
92
+ keys together are at most 65536 bytes. A file is uploaded once, but repeating
93
+ its reference repeats the image in the model's expanded input.
94
+
95
+ The bridge uses the JSON/base64 transport supported by TypeSafe. It does not
96
+ accept multipart `part` assets, read local image files, fetch URLs, resize,
97
+ rotate, or re-encode images. Base64 overhead counts toward the request limit.
98
+ Native Jev assets are separate from the common request shape; use
99
+ `createDecision` for images embedded in instructions or criteria.
100
+
101
+ ## Boxes and results
102
+
103
+ `box(instructions)` takes no criteria. Its answer has `type: 'box'`, `box`,
104
+ and native `confidence`. Coordinates are fractions measured from the top-left:
105
+ `left` and `right` scale with width; `top` and `bottom` scale with height.
106
+ All four values must be finite numbers in 0–1, with `left < right` and
107
+ `top < bottom`. Validation rejects malformed boxes rather than repairing them.
108
+ Common results containing box questions use result `meta.schemaVersion: 4`;
109
+ the CLI configuration schema version remains 1.
110
+
111
+ Each question returns one box. There is no absent-target result: a box is
112
+ returned even when the target is absent. Pair it with a presence `noul` and
113
+ choose your own presence/confidence thresholds using representative images.
114
+ Jev token usage may be `null`; that means unknown, never zero. Native confidence
115
+ and all other result checks remain enforced.
116
+
117
+ ## Image and request limits
118
+
119
+ - PNG or JPEG (`png`, `jpg`, `jpeg`); still images only. Animated PNG is rejected.
120
+ - At most 16 MiB per decoded image file.
121
+ - Each dimension is 256–1568 pixels, and area is 78,400–2,458,624 pixels.
122
+ A 256 × 256 image fails the area minimum.
123
+ - Jev requests default to a 32 MiB serialized JSON ceiling. `maxRequestBytes`
124
+ can lower it, but cannot exceed 32 MiB. Response budgets remain independently
125
+ configurable, with an 8 MiB default.
126
+ - State, each instruction, and each criterion must fit 64 nested JSON levels.
127
+ General bridge snapshots also retain their 200,000-node limit.
128
+
129
+ Preflight checks base64, headers, dimensions, PNG animation, references, original
130
+ image bytes, and serialized request bytes. Full decoding, server re-encoding,
131
+ the 32 MiB processed-image and expanded-input limits, and token limits remain
132
+ service checks. Client header inspection cannot predict processed sizes.
133
+ Prepare upright pixels yourself: TypeSafe removes metadata and does not apply
134
+ EXIF orientation. No separate image-count cap is asserted by the bridge;
135
+ `maxImages: null` means size and context determine capacity.
136
+
137
+ ## CLI
138
+
139
+ ```json
140
+ {
141
+ "schemaVersion": 1,
142
+ "provider": "jev",
143
+ "model": "jev-1.14.0",
144
+ "apiKeyEnv": "TYPESAFE_API_KEY",
145
+ "dangerZone": { "images": true, "longContext": true }
146
+ }
147
+ ```
148
+
149
+ ```shell
150
+ decision-bridge validate request.json --config decision.config.json
151
+ decision-bridge evaluate request.json --config decision.config.json --env-file .env
152
+ decision-bridge validate native.json --native --model jev-preview --jev-images
153
+ decision-bridge batch native.jsonl --native --config decision.config.json --env-file .env
154
+ ```
155
+
156
+ Common/native validation is offline and uses the same preflight as inference.
157
+ `--native` works with `init`, `validate`, `evaluate`, and `batch`. Jev keeps its
158
+ `models` command. CLI JSON/JSONL input supports requests up to 32 MiB per record;
159
+ the actual serialized request must also fit the configured provider ceiling.
160
+
161
+ `--jev-images` / `--no-jev-images` and `--jev-long-context` /
162
+ `--no-jev-long-context` override independent fields in `dangerZone`.
163
+ `DECISION_JEV_IMAGES` and `DECISION_JEV_LONG_CONTEXT` accept exactly `true` or
164
+ `false`. Flags override environment, then JSON configuration, then defaults.
165
+ Switching providers discards Jev-specific settings; using these flags with
166
+ another provider is a configuration error.
167
+
168
+ ## Protocol sources
169
+
170
+ - [TypeSafe release announcement](https://typesafe.ai/blog/jev-1-14)
171
+ - [Images, assets, limits, and experimental header](https://docs.typesafe.ai/images)
172
+ - [Box questions and absence behavior](https://docs.typesafe.ai/primitives/box)
173
+ - [Models, aliases, and context limits](https://docs.typesafe.ai/models)
174
+ - [JavaScript SDK compatibility changes](https://docs.typesafe.ai/sdk/javascript/changelog)
@@ -22,7 +22,7 @@ compatible native Node runtime. See [testing](../development/TESTING.md).
22
22
  Open PowerShell at the cloned repository or extracted source directory:
23
23
 
24
24
  ```powershell
25
- cd "C:\path\to\decision-bridge-0.1.1"
25
+ cd "C:\path\to\decision-bridge-0.1.2"
26
26
  node --version
27
27
  node bin/decision.mjs validate examples/request.json
28
28
  Copy-Item .env.example .env
@@ -48,7 +48,7 @@ the archive rather than reusing generated `node_modules` or executable shims.
48
48
  ## macOS / Linux
49
49
 
50
50
  ```bash
51
- cd "/path/to/decision-bridge-0.1.1"
51
+ cd "/path/to/decision-bridge-0.1.2"
52
52
  node --version
53
53
  node bin/decision.mjs validate examples/request.json
54
54
  cp .env.example .env
@@ -51,6 +51,12 @@ there is no CLI binding selector. Gateway settings route the selected REST reque
51
51
  | `models` | List the configured provider's models; may be paginated |
52
52
  | `batch FILE\|-` | Stream JSONL inputs and ordered JSONL result/error records |
53
53
 
54
+ Jev supports both common and `--native` requests for evaluation, validation,
55
+ initialization, and ordered batches. Experimental images/boxes require
56
+ `--jev-images`, and longer context requires `--jev-long-context` (or matching
57
+ JSON configuration). Use `--no-jev-images` and `--no-jev-long-context` to
58
+ override enabled settings. See the [Jev guide](../guides/JEV.md).
59
+
54
60
  No input filename on `evaluate`, `validate`, or `schema` selects the bundled sample,
55
61
  not stdin. Use `-` explicitly for stdin. Empty invocation prints help. Configuration
56
62
  and credential errors do not cause fallback to another backend.
@@ -91,6 +97,8 @@ later configuration changes to control selection.
91
97
  | `--provider NAME` | `jev`, `openai-decisions`, `cloudflare-decisions`, `custom-decision`, `custom-generative` |
92
98
  | `--preset NAME` | custom-generative preset: `openai-compatible`, `openrouter`, or `anthropic`; exclusive with module |
93
99
  | `--native` | Explicit native Decisions input/output; init/evaluate/validate/batch only |
100
+ | `--jev-images`, `--no-jev-images` | Enable/disable experimental Jev images and boxes |
101
+ | `--jev-long-context`, `--no-jev-long-context` | Enable/disable experimental Jev 262k context |
94
102
  | `--model SELECTOR` | Model selector or supported upstream ID; overrides input JSON model and configured default |
95
103
  | `--base-url URL` | Custom endpoint; requires explicit credential selection |
96
104
  | `--api-key-env NAME` | Environment variable name, never the key itself |
@@ -172,7 +180,10 @@ delivery needs separate validation.
172
180
 
173
181
  ## I/O and resource limits
174
182
 
175
- Single JSON input: **8 MiB**. JSONL: **8 MiB per line, 64 MiB total**. Config:
183
+ Single JSON input normally uses **8 MiB**. JSONL normally uses **8 MiB per line,
184
+ 64 MiB total**. Jev permits **32 MiB** for a single JSON input or JSONL record,
185
+ then checks serialized requests against its configured ceiling (32 MiB by default).
186
+ Config:
176
187
  **1 MiB**. Environment file: **64 KiB**. Provider request and response default to
177
188
  8 MiB each, configurable to at most 64 MiB. Cloudflare reads single JSON inputs
178
189
  and individual JSONL records within 64 MiB, then applies model-specific serialized
@@ -46,10 +46,11 @@ profile. Gateway configuration is optional for either SDK transport and for CLI
46
46
  | `noAuth` | Boolean; true only for loopback OpenAI-compatible endpoints |
47
47
  | `timeoutMs` | Positive integer up to 2,147,483,647 milliseconds |
48
48
  | `maxRetries` | Integer 0–10; OpenAI/Cloudflare default 0; Jev and generative presets default 2 |
49
- | `maxRequestBytes`, `maxResponseBytes` | Positive integer through 64 MiB; default 8 MiB. Cloudflare requests: Clef/Clef-flash default/cap 13 MiB; Omni default 36 MiB, configurable through 64 MiB |
49
+ | `maxRequestBytes`, `maxResponseBytes` | Positive integer through 64 MiB; default 8 MiB. Jev requests default/cap 32 MiB. Cloudflare requests: Clef/Clef-flash default/cap 13 MiB; Omni default 36 MiB, configurable through 64 MiB |
50
50
  | `accountId` | Cloudflare REST account ID; otherwise `CLOUDFLARE_ACCOUNT_ID` |
51
51
  | `rejectIfBusy` | Cloudflare boolean; default false, incompatible with retries. Effective true fails REST preflight with `UNSUPPORTED_CAPABILITY`; see the [binding support and REST limitation](../guides/CLOUDFLARE_DECISIONS.md#capacity-rejection-and-ai-gateway) |
52
52
  | `gateway` | Cloudflare Gateway object; see the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md) |
53
+ | `dangerZone` | Jev-only `{ images?: boolean, longContext?: boolean }`; both default false; see the [Jev guide](../guides/JEV.md) |
53
54
  | `maxTokens` | Generative output budget, 1–1,048,576; default 4096 |
54
55
  | `responseFormat` | Compatible generative presets: `json_schema`, `json_object`, `text` |
55
56
  | `tokenParameter` | Compatible generative presets: `max_completion_tokens` or `max_tokens` |
@@ -58,6 +59,10 @@ profile. Gateway configuration is optional for either SDK transport and for CLI
58
59
  | `options` | Custom factory's JSON options, e.g. its own baseURL |
59
60
 
60
61
  Upper numeric limits validate client configuration, not vendor model capabilities.
62
+ Jev requests default to and are capped at 32 MiB; a smaller `maxRequestBytes`
63
+ lowers that ceiling. `DECISION_JEV_IMAGES` and `DECISION_JEV_LONG_CONTEXT`
64
+ accept `true` or `false`; their CLI overrides are `--jev-images`,
65
+ `--no-jev-images`, `--jev-long-context`, and `--no-jev-long-context`.
61
66
  Cloudflare selects its byte budget from the effective request model, including
62
67
  per-call overrides. An explicit smaller `maxRequestBytes` remains the ceiling;
63
68
  selecting Omni cannot enlarge it. Omni's default is a bridge policy, not a
@@ -44,7 +44,7 @@ contract. The following additional forms are checked before dispatch:
44
44
 
45
45
  | Provider | Ordered text / embedded images | Typed string/boolean choices | Labelled score levels | Safety identifier | Model listing |
46
46
  | --- | --- | --- | --- | --- | --- |
47
- | Jev | Unsupported | Unsupported | Unsupported | Unsupported | Supported |
47
+ | Jev | Ordered text; images with explicit opt-in | Unsupported | Unsupported | Unsupported | Supported |
48
48
  | OpenAI Decisions | Supported; up to 128 images, preserving order | Supported | Supported | Supported | Unsupported |
49
49
  | Cloudflare Clef / Clef-flash | Supported; up to four images before text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
50
50
  | Cloudflare Clef-omni | Supported; up to four images before audio/video/text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
@@ -62,7 +62,11 @@ capability fields `audio`, `videos`, `maxAudioClips`, and `maxVideos` normalize
62
62
  false/zero when omitted, preserving legacy custom profiles. Cloudflare's older
63
63
  models and all other shipped providers reject audio/video before inference.
64
64
 
65
- Dedicated `createDecision()` access is available on OpenAI Decisions and
65
+ Jev additionally supports `box` questions on `jev-preview` and pinned 1.14+ models
66
+ when `dangerZone.images` is enabled. Other built-ins reject box questions.
67
+ See the [Jev guide](../guides/JEV.md) for asset placement and experimental opt-ins.
68
+
69
+ Dedicated `createDecision()` access is available on Jev, OpenAI Decisions and
66
70
  Cloudflare providers. It uses each service's native request/response shape and
67
71
  bypasses bridge preparation and observation hooks.
68
72
 
@@ -74,6 +78,12 @@ intermediate language model, skill, portal prompt, or chat conversion is used.
74
78
  `GET /v1/models` lists native models. A listed catalog can differ from valid aliases;
75
79
  `jev-latest` is the documented stable alias.
76
80
 
81
+ `jev-preview` selects the preview release. Images and longer context require
82
+ explicit `dangerZone` settings. Common image input preserves ordering; native
83
+ `createDecision()` additionally accepts JSON assets referenced in state,
84
+ instructions, and criteria. Box outputs preserve normalized edges and confidence.
85
+ See the [Jev guide](../guides/JEV.md) for limits and absence handling.
86
+
77
87
  Pin a version after evaluating it for your workload rather than assuming a moving
78
88
  alias yields reproducible quality. Store the returned `model` along with the
79
89
  requested model and your question-template revision.
@@ -48,7 +48,7 @@ guides. These are SDK options, not per-evaluation bridge options.
48
48
  | --- | --- |
49
49
  | `timeoutMs` | 30,000 ms; positive integer through 2,147,483,647; one transport budget for dispatch, retries, backoff, and response reading |
50
50
  | `maxRetries` | Additional eligible attempts, 0–10; Jev and generative presets default to 2, native OpenAI and Cloudflare to 0 |
51
- | `maxRequestBytes` | Complete serialized request ceiling; normally 8 MiB, configurable from 1 byte through 64 MiB; Cloudflare applies its [model-specific budgets](../guides/CLOUDFLARE_DECISIONS.md#embedded-images-and-limits) |
51
+ | `maxRequestBytes` | Complete serialized request ceiling; normally 8 MiB, configurable from 1 byte through 64 MiB; Jev defaults to and caps at 32 MiB; Cloudflare applies its [model-specific budgets](../guides/CLOUDFLARE_DECISIONS.md#embedded-images-and-limits) |
52
52
  | `maxResponseBytes` | 8 MiB; configurable from 1 byte through 64 MiB; applies while reading the response body |
53
53
  | `fetchImpl` | Optional `(url: string, init?: RequestInit) => Promise<Response>`; defaults to the runtime's `fetch` |
54
54
 
@@ -146,6 +146,10 @@ const q3 = score('How far did processing progress?', [
146
146
  ]);
147
147
  ```
148
148
 
149
+ `box(instructions)` returns a box question with no criteria; supported providers
150
+ must declare `boxes: true`. Jev supports it with experimental image opt-in and
151
+ a supported model; see [Jev images and boxes](../guides/JEV.md).
152
+
149
153
  Builders return immutable question objects. `choice(instructions, criteria)` accepts
150
154
  a label map containing 1 to 255 options. `noul(instructions, criteria?)` accepts an
151
155
  optional criteria object,
@@ -201,7 +205,8 @@ rubric exactly. Generative providers calculate score and legend locally. Native
201
205
  values are validated and preserved, not normalized or replaced.
202
206
 
203
207
  **Usage:** `input_tokens` and `output_tokens` are nonnegative integers or null.
204
- Jev, OpenAI Decisions, and Cloudflare require reported integer counts. OpenAI and
208
+ Jev accepts explicitly null counts as unknown; OpenAI Decisions and Cloudflare
209
+ require reported integer counts. OpenAI and
205
210
  Anthropic cache creation/read counts are preserved separately when provided;
206
211
  Cloudflare does not fabricate cache or total-token fields. No total price, inferred cache saving, or
207
212
  estimated token count is fabricated.
@@ -231,7 +236,8 @@ unavailable metadata exists. Result objects and nested data returned by the brid
231
236
  are immutable. Standalone validation functions return snapshots, not frozen values.
232
237
 
233
238
  `schemaVersion` remains 1 for legacy requests and 2 for ordered images/text or
234
- richer question forms. Audio/video requests use 3. This result version is separate
239
+ richer question forms. Audio/video requests use 3; requests with boxes use 4.
240
+ This result version is separate
235
241
  from the CLI configuration schema, which remains 1.
236
242
 
237
243
  ## Extensions
@@ -304,6 +310,8 @@ automatic model fallback. The CLI has a separate streaming JSONL implementation.
304
310
  - `createProvider(config)`: factory requiring an explicit `type` equal to `jev`,
305
311
  `openai-decisions`, or `cloudflare-decisions`.
306
312
  - `JevProvider`, `OpenAIDecisionsProvider`, `CloudflareDecisionsProvider`: native provider classes.
313
+ - `box(instructions)` and `JevProvider.createDecision(request, options?)`:
314
+ bounding boxes and native JSON assets; see the [Jev guide](../guides/JEV.md).
307
315
  - `scoreLevels(instructions, levels)` and the array overload of `choice`:
308
316
  labelled rubrics and typed string/boolean options, with provider capability checks.
309
317
  - `OpenAIDecisionsProvider.createDecision(request, options?)`: dedicated native
@@ -314,9 +322,9 @@ automatic model fallback. The CLI has a separate streaming JSONL implementation.
314
322
  an own `input.model` property or an explicitly supplied default; inherited
315
323
  models are ignored, and it never selects a Jev model.
316
324
  Question builders and schema generation validate without selecting an inference model.
317
- - `validateResult(input, questions, { native = false, probabilityDecimalPlaces })`: snapshot supplied
325
+ - `validateResult(input, questions, { native = false, nullableUsage = false, probabilityDecimalPlaces })`: snapshot supplied
318
326
  questions and validate the canonical result; native mode requires confidence
319
- and reported counts.
327
+ and reported counts unless the provider explicitly enables nullable usage.
320
328
  - `decideChoice(answer, { minProbability, minMargin = 0, abstainOptions = [], probabilityDecimalPlaces })`:
321
329
  returns a policy result, never executes an action. `minProbability` is required.
322
330
  - `DecisionError`, `publicError(error)`: public structured failure handling.
@@ -11,6 +11,10 @@ reference does not imply that authenticated inference was run against its servic
11
11
  | TypeSafe request/response API | https://docs.typesafe.ai/api |
12
12
  | TypeSafe JavaScript examples | https://docs.typesafe.ai/sdk/javascript |
13
13
  | Native model listing and aliases | https://docs.typesafe.ai/models |
14
+ | Jev image assets and experimental opt-in | https://docs.typesafe.ai/images |
15
+ | Jev bounding boxes | https://docs.typesafe.ai/primitives/box |
16
+ | Jev 1.14 release | https://typesafe.ai/blog/jev-1-14 |
17
+ | Jev nullable usage compatibility | https://docs.typesafe.ai/sdk/javascript/changelog |
14
18
  | Choice decisions | https://docs.typesafe.ai/primitives/choice |
15
19
  | Noul probabilities | https://docs.typesafe.ai/primitives/noul |
16
20
  | Score rubric and weighted expectation | https://docs.typesafe.ai/primitives/score |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fayegram-ai/decision-bridge",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "JavaScript SDK and CLI for structured decisions across native decision APIs and configurable model providers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,13 +29,27 @@
29
29
  "$ref": "#/$defs/retries"
30
30
  },
31
31
  "maxRequestBytes": {
32
- "$ref": "#/$defs/bytes"
32
+ "type": "integer",
33
+ "minimum": 1,
34
+ "maximum": 33554432
33
35
  },
34
36
  "maxResponseBytes": {
35
37
  "$ref": "#/$defs/bytes"
36
38
  },
37
39
  "provider": {
38
40
  "const": "jev"
41
+ },
42
+ "dangerZone": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "properties": {
46
+ "images": {
47
+ "type": "boolean"
48
+ },
49
+ "longContext": {
50
+ "type": "boolean"
51
+ }
52
+ }
39
53
  }
40
54
  }
41
55
  },
@@ -2,6 +2,7 @@ import { DecisionError, requireCondition as check } from './errors.mjs';
2
2
  import { cloneJson, freezeJson, isRecord } from './validation.mjs';
3
3
  import { CLOUDFLARE_MODELS } from './providers/cloudflare-profile.mjs';
4
4
  import { openaiModelProfile } from './providers/openai-profile.mjs';
5
+ import { jevFeatures } from './providers/jev-profile.mjs';
5
6
 
6
7
  /** Conservative defaults preserve the pre-existing custom-provider contract. */
7
8
  export const LEGACY_CAPABILITIES = freezeJson({
@@ -9,6 +10,7 @@ export const LEGACY_CAPABILITIES = freezeJson({
9
10
  labeledScores: false, safetyIdentifier: false,
10
11
  maxImages: 0, maxChoiceOptions: 255, maxScoreLevels: 10,
11
12
  audio: false, videos: false, maxAudioClips: 0, maxVideos: 0,
13
+ boxes: false,
12
14
  });
13
15
 
14
16
  const CLOUDFLARE_CAPABILITIES = freezeJson({
@@ -16,7 +18,9 @@ const CLOUDFLARE_CAPABILITIES = freezeJson({
16
18
  labeledScores: true, safetyIdentifier: false, maxImages: 4, maxChoiceOptions: 255, maxScoreLevels: 10,
17
19
  });
18
20
 
19
- export function builtInCapabilities(type, model) {
21
+ export function builtInCapabilities(type, model, options = {}) {
22
+ if (type === 'jev') return freezeJson({ ...LEGACY_CAPABILITIES,
23
+ ...jevFeatures(model, Object.hasOwn(options, 'dangerZone') ? options.dangerZone : undefined) });
20
24
  if (type === 'cloudflare-decisions') {
21
25
  const key = typeof model === 'string' ? model.trim().replace(/^@cf\/cloudflare\//, '') : '';
22
26
  const profile = Object.hasOwn(CLOUDFLARE_MODELS, key) ? CLOUDFLARE_MODELS[key] : undefined;
@@ -45,7 +49,7 @@ export function providerCapabilities(provider, model) {
45
49
  check(isRecord(profile) && Object.keys(profile).every(key => Object.hasOwn(LEGACY_CAPABILITIES, key)),
46
50
  'Invalid capability profile.', 'CONFIGURATION_ERROR');
47
51
  const result = { ...LEGACY_CAPABILITIES, ...profile };
48
- for (const key of ['supported', 'orderedInput', 'images', 'typedChoices', 'labeledScores', 'safetyIdentifier', 'audio', 'videos']) {
52
+ for (const key of ['supported', 'orderedInput', 'images', 'typedChoices', 'labeledScores', 'safetyIdentifier', 'audio', 'videos', 'boxes']) {
49
53
  check(typeof result[key] === 'boolean', 'Capability flags must be boolean.', 'CONFIGURATION_ERROR');
50
54
  }
51
55
  for (const key of ['maxImages', 'maxChoiceOptions', 'maxScoreLevels', 'maxAudioClips', 'maxVideos']) {
@@ -75,6 +79,7 @@ export function assertCapabilities(request, capabilities) {
75
79
  }
76
80
  if (Object.hasOwn(request, 'safetyIdentifier')) requireFeature(capabilities.safetyIdentifier, 'safetyIdentifier');
77
81
  for (const q of Object.values(request.questions)) {
82
+ if (q.type === 'box') requireFeature(capabilities.boxes, 'box questions');
78
83
  if (q.options) requireFeature(capabilities.typedChoices, 'typed choices');
79
84
  if (q.levels) requireFeature(capabilities.labeledScores, 'labelled scores');
80
85
  const choices = q.options?.length ?? (q.type === 'choice' ? Object.keys(q.criteria).length : 0);
@@ -86,6 +91,7 @@ export function assertCapabilities(request, capabilities) {
86
91
  }
87
92
 
88
93
  export function commonSchemaVersion(request) {
94
+ if (Object.values(request.questions).some(q => q.type === 'box')) return 4;
89
95
  if (request.input?.some(part => ['audio', 'video'].includes(part.type))) return 3;
90
96
  return Object.hasOwn(request, 'input') || Object.hasOwn(request, 'safetyIdentifier') ||
91
97
  Object.values(request.questions).some(q => q.options || q.levels) ? 2 : 1;
@@ -2,13 +2,14 @@ import { requireCondition as check } from '../errors.mjs';
2
2
  import { cloneJson, isRecord } from '../validation.mjs';
3
3
  import { cloudflareGateway, validateCloudflareLimits } from '../providers/cloudflare-profile.mjs';
4
4
  import { generationOptions } from '../custom/generative/shared.mjs';
5
+ import { jevDangerZone, jevRequestLimit } from '../providers/jev-profile.mjs';
5
6
 
6
7
  export const PROVIDERS = ['jev', 'openai-decisions', 'cloudflare-decisions', 'custom-decision', 'custom-generative'];
7
8
  export const PRESETS = ['openai-compatible', 'openrouter', 'anthropic'];
8
9
  export const TRANSPORT = ['baseURL', 'maxRetries', 'maxRequestBytes', 'maxResponseBytes'];
9
10
  export const GENERATION = ['maxTokens', 'responseFormat', 'tokenParameter', 'parameters', 'noAuth'];
10
11
  export const PROVIDER_FIELDS = [...TRANSPORT, ...GENERATION, 'apiKeyEnv', 'model', 'preset', 'module',
11
- 'options', 'accountId', 'rejectIfBusy', 'gateway'];
12
+ 'options', 'accountId', 'rejectIfBusy', 'gateway', 'dangerZone'];
12
13
  const FIELDS = new Set(['$schema', 'schemaVersion', 'provider', 'timeoutMs', ...PROVIDER_FIELDS]);
13
14
  export const NUMERIC = new Set(['timeoutMs', 'maxRetries', 'maxRequestBytes', 'maxResponseBytes', 'maxTokens']);
14
15
  export const KEY_ENVS = { jev: 'TYPESAFE_API_KEY', 'openai-decisions': 'OPENAI_API_KEY',
@@ -56,7 +57,14 @@ export function validateConfig(input, { complete = false } = {}) {
56
57
  check(config.module === undefined || ['custom-decision', 'custom-generative'].includes(config.provider),
57
58
  'module requires custom-decision or custom-generative.', 'CONFIGURATION_ERROR');
58
59
  check(config.options === undefined || isModule(config), 'options requires a custom provider module.', 'CONFIGURATION_ERROR');
60
+ // Validate nested shape even when a partial file omits its provider selector.
61
+ if (Object.hasOwn(config, 'dangerZone')) config.dangerZone = jevDangerZone(config.dangerZone);
59
62
  if (config.provider !== undefined) {
63
+ if (config.provider !== 'jev') check(!Object.hasOwn(config, 'dangerZone'),
64
+ 'dangerZone requires Jev.', 'CONFIGURATION_ERROR');
65
+ else {
66
+ jevRequestLimit(config.maxRequestBytes);
67
+ }
60
68
  if (config.provider !== 'cloudflare-decisions') check(['accountId', 'rejectIfBusy', 'gateway'].every(key => config[key] === undefined),
61
69
  'Cloudflare settings require cloudflare-decisions.', 'CONFIGURATION_ERROR');
62
70
  else {