@fayegram-ai/decision-bridge 0.1.1 → 0.1.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/CONTRIBUTING.md +5 -3
  3. package/README.md +26 -10
  4. package/SECURITY.md +18 -11
  5. package/docs/README.md +19 -5
  6. package/docs/development/ARCHITECTURE.md +36 -7
  7. package/docs/development/DESIGN.md +21 -3
  8. package/docs/development/RELEASING.md +7 -3
  9. package/docs/development/REPOSITORY_LAYOUT.md +2 -0
  10. package/docs/development/TESTING.md +31 -3
  11. package/docs/guides/BROWSER.md +8 -0
  12. package/docs/guides/CLOUDFLARE_DECISIONS.md +24 -46
  13. package/docs/guides/CUSTOMIZATION.md +8 -2
  14. package/docs/guides/CUSTOM_GENERATIVE.md +5 -3
  15. package/docs/guides/CUSTOM_PROVIDERS.md +5 -3
  16. package/docs/guides/EXTRACTION.md +125 -0
  17. package/docs/guides/GETTING_STARTED.md +8 -4
  18. package/docs/guides/JEV.md +167 -0
  19. package/docs/guides/LIVE_DECISIONS.md +163 -0
  20. package/docs/guides/OPENAI_DECISIONS.md +52 -24
  21. package/docs/guides/PLATFORMS.md +3 -3
  22. package/docs/guides/SWITCHING_MODELS.md +6 -2
  23. package/docs/guides/TROUBLESHOOTING.md +25 -0
  24. package/docs/reference/CLI.md +60 -11
  25. package/docs/reference/CONFIGURATION.md +34 -6
  26. package/docs/reference/ERRORS.md +41 -12
  27. package/docs/reference/PROVIDERS.md +31 -8
  28. package/docs/reference/SDK.md +72 -26
  29. package/docs/reference/SOURCES.md +11 -0
  30. package/examples/extraction.mjs +16 -0
  31. package/examples/live-decisions.mjs +24 -0
  32. package/package.json +11 -1
  33. package/schemas/decision.config.schema.json +102 -11
  34. package/src/bridge.mjs +9 -6
  35. package/src/capabilities.mjs +34 -4
  36. package/src/cli/config-validation.mjs +13 -5
  37. package/src/cli/config.mjs +19 -4
  38. package/src/cli/decisions.mjs +8 -0
  39. package/src/cli/initialize.mjs +8 -4
  40. package/src/cli/live.mjs +63 -0
  41. package/src/cli/main.mjs +50 -16
  42. package/src/cli/provider-loader.mjs +3 -2
  43. package/src/cloudflare-decisions.d.mts +12 -5
  44. package/src/decisions.d.mts +28 -4
  45. package/src/errors.mjs +7 -1
  46. package/src/extraction/index.d.mts +66 -0
  47. package/src/extraction/index.mjs +78 -0
  48. package/src/extraction/validation.mjs +105 -0
  49. package/src/index.d.mts +42 -9
  50. package/src/index.mjs +1 -1
  51. package/src/input.mjs +21 -3
  52. package/src/jev.d.mts +16 -0
  53. package/src/live/index.d.mts +73 -0
  54. package/src/live/index.mjs +170 -0
  55. package/src/live/state.mjs +125 -0
  56. package/src/live/validation.mjs +122 -0
  57. package/src/probabilities.mjs +6 -3
  58. package/src/providers/cloudflare-images.mjs +5 -67
  59. package/src/providers/cloudflare-media.mjs +5 -10
  60. package/src/providers/cloudflare-preflight.mjs +4 -3
  61. package/src/providers/cloudflare-profile.mjs +10 -14
  62. package/src/providers/cloudflare-transport.mjs +12 -5
  63. package/src/providers/cloudflare-validation.mjs +3 -1
  64. package/src/providers/decisions-codec.mjs +10 -5
  65. package/src/providers/decisions-input.mjs +120 -0
  66. package/src/providers/decisions-validation.mjs +37 -69
  67. package/src/providers/image-header.mjs +59 -0
  68. package/src/providers/jev-images.mjs +84 -0
  69. package/src/providers/jev-preflight.mjs +62 -0
  70. package/src/providers/jev-profile.mjs +37 -0
  71. package/src/providers/jev.mjs +32 -13
  72. package/src/providers/media-binary.mjs +0 -7
  73. package/src/providers/openai-decisions.mjs +2 -1
  74. package/src/providers/openai-errors.mjs +23 -0
  75. package/src/providers/openai-profile.mjs +2 -1
  76. package/src/questions.mjs +2 -0
  77. package/src/response-transport.mjs +84 -17
  78. package/src/validation.mjs +39 -20
  79. package/src/version.mjs +1 -1
@@ -1,7 +1,9 @@
1
1
  # Errors and recovery
2
2
 
3
3
  `DecisionError` extends `Error` and exposes `code`, optional `status`, `requestId`,
4
- `gatewayLogId`, `attempts`, and numeric `upstreamCode` for Cloudflare diagnostics.
4
+ `gatewayLogId`, `attempts`, and numeric `upstreamCode` for Cloudflare diagnostics. Optional `source`,
5
+ `upstreamErrorCode`, `upstreamErrorType`, `retryAfterMs` and `retryable`
6
+ distinguish client controls and upstream failures.
5
7
  OpenAI Decisions refusal errors can also carry validated `refusedQuestions` and
6
8
  `usage`. `toJSON()` and `publicError(error)` return safe structured details.
7
9
  Unknown exceptions become a generic message, not a copied stack/body. Custom
@@ -16,13 +18,13 @@ providers deliberately constructing `DecisionError` must keep its message safe.
16
18
  | `USAGE_ERROR` | Invalid CLI flags/arguments or stdin usage; consult help |
17
19
  | `IO_ERROR` | Input/output path or permissions failure; preserve previous output |
18
20
  | `INPUT_TOO_LARGE` | CLI byte limit exceeded; reduce or split input |
19
- | `REQUEST_TOO_LARGE` | Encoded provider request exceeds configured limit; reduce input/schema |
21
+ | `REQUEST_TOO_LARGE` | Configured client byte budget or upstream size admission rejected the request; inspect `source` and optional status |
20
22
  | `RESPONSE_TOO_LARGE` | Provider response exceeds configured limit; inspect budget/model behavior |
21
23
  | `HTTP_ERROR` | Non-success HTTP response; use status/request ID, not raw error body |
22
24
  | `CAPACITY_EXCEEDED` | Cloudflare capacity unavailable (3040); busy mode does not queue/retry |
23
- | `ALLOCATION_EXCEEDED` | Cloudflare allocation limit exceeded (3036) |
24
- | `RATE_LIMITED` | Cloudflare HTTP 429 without a more specific code |
25
- | `AUTHENTICATION_ERROR`, `PERMISSION_DENIED` | Cloudflare authentication or authorization failure |
25
+ | `ALLOCATION_EXCEEDED` | Provider allocation, credit or spending limit exceeded; not automatically retried |
26
+ | `RATE_LIMITED` | Temporary provider throttling without a more specific allocation/capacity code |
27
+ | `AUTHENTICATION_ERROR`, `PERMISSION_DENIED` | Provider authentication or authorization failure |
26
28
  | `UPSTREAM_TIMEOUT`, `UPSTREAM_ABORTED` | Cloudflare upstream timeout (3007) or abort (3008) |
27
29
  | `NETWORK_ERROR` | Transport failed ambiguously; no automatic network retry occurred |
28
30
  | `RETRY_BUDGET_EXCEEDED` | Server delay cannot fit remaining deadline; choose a later attempt explicitly |
@@ -35,6 +37,10 @@ providers deliberately constructing `DecisionError` must keep its message safe.
35
37
  | `PROVIDER_ERROR` | Sanitized custom-provider exception |
36
38
  | `UNSUPPORTED_OPERATION` | Provider does not implement listing or another requested operation |
37
39
  | `BATCH_SKIPPED` | SDK stop-on-error prevented a not-yet-started record |
40
+ | `INVALID_REQUEST` | Upstream request rejection; inspect safe machine code/type and correct the request |
41
+ | `CONNECTION_CLOSED`, `DISPOSED` | Live connection ended or controller was disposed; pending work is cancelled |
42
+ | `RESOURCE_LIMIT` | Live client pending-operation budget exceeded; settle existing operations before submitting more |
43
+ | `OBSERVER_ERROR` | Live observer notification failed; no command or application action is repeated |
38
44
  | `UNEXPECTED_ERROR` | Unrecognized error sanitized for public output |
39
45
 
40
46
  ## Retry policy
@@ -44,18 +50,27 @@ available HTTP diagnostics. A refusal of any question fails that complete call;
44
50
  successful siblings are not exposed as a partial result.
45
51
 
46
52
  The shared response transport retries statuses **429, 502, 503, 504, and 529**
47
- only, for both HTTP requests and Workers AI binding responses. `maxRetries`
53
+ as a fallback for HTTP and binding responses. Provider decoders refine eligibility; known allocation/billing failures are never retried. `maxRetries`
48
54
  means additional attempts; the default 2 allows at most 3 total attempts. Native
49
- OpenAI Decisions and Cloudflare Clef default to 0; explicitly enabling retries can repeat a billed
55
+ OpenAI Decisions, Cloudflare Clef and Clairic default to 0; explicitly enabling retries can repeat a billed
50
56
  request. All
51
57
  attempts, backoff, and response-body reading share one timeout budget.
52
58
 
53
- `Retry-After` seconds or HTTP dates are honored. Without it, exponential backoff
59
+ Valid `Retry-After` decimal integer seconds or HTTP dates are honored. All three
60
+ HTTP date forms are interpreted in UTC; an elapsed date means zero delay.
61
+ Malformed values are ignored, including fractional, signed, hexadecimal and
62
+ exponent numbers. Without a valid hint, exponential backoff
54
63
  starts at 250 ms with jitter and an 8-second base cap. A delay that cannot fit the
55
64
  remaining budget fails instead of violating the server's requested wait.
56
65
 
57
- Authentication failures, generic HTTP 500, invalid output, model refusal, and
58
- ambiguous network failures are not retried automatically. HTTP retries are not an
66
+ When an HTTP rejection body is malformed, too large, or interrupted, the known
67
+ status, safe request/Gateway IDs and valid `Retry-After` remain available. If the
68
+ body cannot be decoded, classification and retry eligibility use the status
69
+ alone; no missing upstream code is invented. Caller cancellation and the total
70
+ deadline still take precedence over this fallback.
71
+
72
+ Authentication failures, invalid output, model refusal, and
73
+ ambiguous network failures are not retried automatically. OpenAI may classify HTTP 500 as retryable; other transports retain their fallback policy. HTTP retries are not an
59
74
  exactly-once billing guarantee. Set `maxRetries: 0` when your workflow requires
60
75
  manual retry decisions. The bridge never retries a successful request because an
61
76
  observer failed; inspect `meta.observerFailures` separately.
@@ -64,8 +79,9 @@ observer failed; inspect `meta.observerFailures` separately.
64
79
 
65
80
  401/403 usually requires checking key/account/model authorization with the provider;
66
81
  the client reports the actual status rather than claiming to diagnose account
67
- state. 404 can indicate an incorrect API base/model/route. 429 exposes throttling;
68
- repeated retries can increase load. For `INVALID_RESPONSE`, a schema-capable model
82
+ state. 404 can indicate an incorrect API base/model/route. A 429 can represent
83
+ temporary throttling or an allocation/billing failure; inspect `code`,
84
+ `upstreamErrorCode`, `upstreamErrorType` and `retryable`. For `INVALID_RESPONSE`, a schema-capable model
69
85
  may still emit invalid probabilities or be routed through an incompatible endpoint.
70
86
  Change configuration deliberately and retest; do not disable local validation.
71
87
 
@@ -83,3 +99,16 @@ For successful OpenAI calls, retain
83
99
  the requested selection, resolved outgoing ID, and reported model separately;
84
100
  see [model identities](../guides/OPENAI_DECISIONS.md#model-selection). Log input or results only under your
85
101
  own data-handling policy. No logging or telemetry is enabled by default.
102
+
103
+ ## Extraction and Live
104
+
105
+ Clairic preserves native nulls, empty lists and the `truncated` flag in a successful
106
+ extraction envelope; truncation is not converted into a decision-style failure.
107
+ The application decides whether that extraction is sufficient for its task.
108
+
109
+ Live preserves native refusal answers on completed decision events. Malformed
110
+ server events report `INVALID_RESPONSE`; the offline event-file validator reports
111
+ caller input as `VALIDATION_ERROR`. Observer failures produce `observer_error`
112
+ notifications containing a safe `OBSERVER_ERROR`. Command submission receipts do
113
+ not promise a completed decision. No Live command or application action is
114
+ automatically replayed. See [Live lifecycle](../guides/LIVE_DECISIONS.md).
@@ -44,25 +44,35 @@ 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 |
48
- | OpenAI Decisions | Supported; up to 128 images, preserving order | Supported | Supported | Supported | Unsupported |
49
- | Cloudflare Clef / Clef-flash | Supported; up to four images before text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
50
- | Cloudflare Clef-omni | Supported; up to four images before audio/video/text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
47
+ | Jev | Ordered text; images with explicit opt-in | Unsupported | Unsupported | Unsupported | Supported |
48
+ | OpenAI Decisions | Supported; preserving order | Supported | Supported | Supported | Unsupported |
49
+ | Cloudflare Clef / Clef-flash | Supported; images before text | Supported | Supported; ordered levels | Unsupported | Unsupported |
50
+ | Cloudflare Clef-omni | Supported; images before audio/video/text | Supported | Supported; ordered levels | Unsupported | Unsupported |
51
51
  | OpenAI-compatible / OpenRouter / Anthropic | Unsupported | Unsupported | Unsupported | Unsupported | Supported; service-specific catalog |
52
52
 
53
53
  `bridge.getCapabilities(model)` reports the local profile, and
54
54
  `bridge.validate(request)` checks that profile and provider preflight without
55
55
  inference. These methods do not authenticate an account or query availability.
56
- Legacy choice maps and score rubrics have their own limits, described in the
56
+ Legacy choice maps and score rubrics have structural requirements, described in the
57
57
  [SDK reference](SDK.md#question-builders). Custom providers declare additional
58
58
  capabilities explicitly.
59
59
 
60
- Omni additionally accepts up to four audio clips and two videos. The optional
61
- capability fields `audio`, `videos`, `maxAudioClips`, and `maxVideos` normalize to
60
+ Omni additionally accepts embedded audio clips and videos; service counts remain
61
+ upstream. The optional capability fields `audio`, `videos`, `maxAudioClips`, and
62
+ `maxVideos` normalize to
62
63
  false/zero when omitted, preserving legacy custom profiles. Cloudflare's older
63
64
  models and all other shipped providers reject audio/video before inference.
64
65
 
65
- Dedicated `createDecision()` access is available on OpenAI Decisions and
66
+ Jev additionally supports `box` questions on `jev-preview` and pinned 1.14+ models
67
+ when `dangerZone.images` is enabled. Other built-ins reject box questions.
68
+ See the [Jev guide](../guides/JEV.md) for asset placement and experimental opt-ins.
69
+
70
+ OpenAI additionally supports `conversation`, `toolHistory`, `remoteImages` and
71
+ `promptCaching`. These optional capability flags default to false for existing
72
+ custom providers. Built-in quantitative limits use `null` where admission is
73
+ left to the service; explicit custom-provider limits remain enforced.
74
+
75
+ Dedicated `createDecision()` access is available on Jev, OpenAI Decisions and
66
76
  Cloudflare providers. It uses each service's native request/response shape and
67
77
  bypasses bridge preparation and observation hooks.
68
78
 
@@ -74,6 +84,12 @@ intermediate language model, skill, portal prompt, or chat conversion is used.
74
84
  `GET /v1/models` lists native models. A listed catalog can differ from valid aliases;
75
85
  `jev-latest` is the documented stable alias.
76
86
 
87
+ `jev-preview` selects the preview release. Images and longer context require
88
+ explicit `dangerZone` settings. Common image input preserves ordering; native
89
+ `createDecision()` additionally accepts JSON assets referenced in state,
90
+ instructions, and criteria. Box outputs preserve normalized edges and confidence.
91
+ See the [Jev guide](../guides/JEV.md) for limits and absence handling.
92
+
77
93
  Pin a version after evaluating it for your workload rather than assuming a moving
78
94
  alias yields reproducible quality. Store the returned `model` along with the
79
95
  requested model and your question-template revision.
@@ -114,3 +130,10 @@ do not override them. This release has no silent provider failover, model search
114
130
  credential refresh, provider-side batch API, or universal quality guarantee.
115
131
  Unsupported protocols require an explicit custom decision or generative provider; unsupported server
116
132
  features require an explicitly supported mode or a different model.
133
+
134
+ ## Dedicated operations
135
+
136
+ Clairic is a native extraction model exposed through [extraction](../guides/EXTRACTION.md).
137
+ Its provider is not a DecisionProvider and is not accepted by createBridge.
138
+ [Live Decisions](../guides/LIVE_DECISIONS.md) owns a connection-oriented command
139
+ and event lifecycle through a dedicated export.
@@ -7,6 +7,9 @@ TypeScript setup, and CommonJS dynamic import. The source-tree entry point is
7
7
  TypeScript declarations are in `src/index.d.mts`; runtime JavaScript is authoritative
8
8
  and checked independently of those declarations.
9
9
 
10
+ This reference covers **0.1.3**. See
11
+ [version availability](../README.md#version-availability) for earlier versions.
12
+
10
13
  ## Create a bridge
11
14
 
12
15
  ```javascript
@@ -48,7 +51,7 @@ guides. These are SDK options, not per-evaluation bridge options.
48
51
  | --- | --- |
49
52
  | `timeoutMs` | 30,000 ms; positive integer through 2,147,483,647; one transport budget for dispatch, retries, backoff, and response reading |
50
53
  | `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) |
54
+ | `maxRequestBytes` | Complete serialized request ceiling; normally 8 MiB, configurable from 1 byte through 64 MiB; Jev defaults to 32 MiB, with explicit client budgets through 64 MiB; Cloudflare applies its [model-specific budgets](../guides/CLOUDFLARE_DECISIONS.md#embedded-images-and-limits) |
52
55
  | `maxResponseBytes` | 8 MiB; configurable from 1 byte through 64 MiB; applies while reading the response body |
53
56
  | `fetchImpl` | Optional `(url: string, init?: RequestInit) => Promise<Response>`; defaults to the runtime's `fetch` |
54
57
 
@@ -68,8 +71,9 @@ they interact; [errors](ERRORS.md#retry-policy) lists retryable statuses.
68
71
 
69
72
  ### `bridge.evaluate(request, options?)`
70
73
 
71
- `request` has exactly one of `state` or ordered `input`, `questions`, and optional
72
- `model` and `safetyIdentifier`. New forms require declared provider capabilities;
74
+ `request` has exactly one of `state`, ordered `input` or `conversation`, plus
75
+ `questions`. Optional fields are `model`, `safetyIdentifier`, `promptCacheKey`
76
+ and `promptCacheOptions`. New forms require declared provider capabilities;
73
77
  see [OpenAI Decisions](../guides/OPENAI_DECISIONS.md) and
74
78
  [Cloudflare Clef](../guides/CLOUDFLARE_DECISIONS.md). The request model
75
79
  wins over the provider's configured default, which an omitted request model inherits.
@@ -81,14 +85,20 @@ resolve to upstream IDs within the provider. No default is assumed for a generat
81
85
  independent transport timeout, including Workers AI binding calls.
82
86
 
83
87
  Ordered parts are `{ type: 'text', text }`, `{ type: 'image', dataUrl, detail? }`,
84
- `{ type: 'audio', dataUrl }`, or `{ type: 'video', dataUrl }`. Audio/video are
85
- supported by Cloudflare Clef-omni and explicitly capable custom providers; other
88
+ `{ type: 'image', url, detail? }`, `{ type: 'audio', dataUrl }`, or
89
+ `{ type: 'video', dataUrl }`. Image `url` and `dataUrl` are exclusive. Text/image
90
+ parts may carry `promptCacheBreakpoint`. Remote HTTP(S) images and cache controls
91
+ require their respective capabilities and are forwarded without URL fetching or
92
+ cache-policy selection. `conversation` uses the native Decisions input-item union;
93
+ see [conversation and prompt caching](../guides/OPENAI_DECISIONS.md#conversation-and-prompt-caching).
94
+ Audio/video are supported by Cloudflare Clef-omni and explicitly capable custom providers; other
86
95
  built-ins reject them before dispatch. Cloudflare requires images → audio →
87
- videos → text. Media use embedded data URLs, with format and size checks described
88
- in the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md#omni-audio-and-video).
96
+ videos → text. Its media use embedded data URLs, with format and client-budget
97
+ checks described in the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md#omni-audio-and-video).
89
98
 
90
- The bridge snapshots and freezes its input, runs sequential state or input preparation,
91
- invokes the provider once, validates the result, then observes an immutable result.
99
+ The bridge snapshots and freezes its input, runs preparation hooks for the
100
+ selected input form, invokes the provider once, validates the result, then
101
+ observes an immutable result.
92
102
  Built-in transports can retry eligible response statuses within that invocation
93
103
  when client retries are configured.
94
104
 
@@ -146,19 +156,22 @@ const q3 = score('How far did processing progress?', [
146
156
  ]);
147
157
  ```
148
158
 
159
+ `box(instructions)` returns a box question with no criteria; supported providers
160
+ must declare `boxes: true`. Jev supports it with experimental image opt-in and
161
+ a supported model; see [Jev images and boxes](../guides/JEV.md).
162
+
149
163
  Builders return immutable question objects. `choice(instructions, criteria)` accepts
150
- a label map containing 1 to 255 options. `noul(instructions, criteria?)` accepts an
164
+ a nonempty label map. `noul(instructions, criteria?)` accepts an
151
165
  optional criteria object,
152
- with only `true` and `false` keys. `score(instructions, criteria)` accepts **2–10**
166
+ with only `true` and `false` keys. `score(instructions, criteria)` accepts at least two
153
167
  ordered levels; numeric level indices are zero-based. There is no boolean alias:
154
168
  `noul` means a probability, not a thresholded truth value.
155
169
 
156
170
  `choice(instructions, options)` accepts ordered objects with unique string or
157
171
  boolean `value` fields and optional text or structured JSON descriptions. Boolean `true` and string
158
172
  `"true"` remain distinct. `scoreLevels(instructions, levels)` accepts at least two
159
- ordered label/description objects. Provider preflight applies service limits:
160
- OpenAI Decisions and Cloudflare require 2–255 choices; Cloudflare limits scores
161
- to 2–10 levels, while OpenAI labelled scores have no additional client count cap.
173
+ ordered label/description objects. Provider preflight enforces structural requirements:
174
+ OpenAI Decisions and Cloudflare require at least two choices; maximum service counts remain upstream.
162
175
 
163
176
  ## Returned data
164
177
 
@@ -201,7 +214,8 @@ rubric exactly. Generative providers calculate score and legend locally. Native
201
214
  values are validated and preserved, not normalized or replaced.
202
215
 
203
216
  **Usage:** `input_tokens` and `output_tokens` are nonnegative integers or null.
204
- Jev, OpenAI Decisions, and Cloudflare require reported integer counts. OpenAI and
217
+ Jev accepts explicitly null counts as unknown; OpenAI Decisions and Cloudflare
218
+ require reported integer counts. OpenAI and
205
219
  Anthropic cache creation/read counts are preserved separately when provided;
206
220
  Cloudflare does not fabricate cache or total-token fields. No total price, inferred cache saving, or
207
221
  estimated token count is fabricated.
@@ -228,20 +242,26 @@ estimated token count is fabricated.
228
242
 
229
243
  Metadata can have additional JSON fields. Application logic should not assume
230
244
  unavailable metadata exists. Result objects and nested data returned by the bridge
231
- are immutable. Standalone validation functions return snapshots, not frozen values.
245
+ are immutable. Standalone common `validateRequest` and `validateResult` return
246
+ snapshots without freezing them; dedicated extraction and Live validators return
247
+ frozen snapshots.
232
248
 
233
- `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
235
- from the CLI configuration schema, which remains 1.
249
+ `schemaVersion` is selected by the highest applicable feature: 5 for conversation,
250
+ remote-image URLs or prompt-cache controls; 4 for boxes; 3 for audio/video; 2 for
251
+ ordered images/text, typed choices, labelled scores or a safety identifier; and
252
+ 1 for legacy requests. The CLI configuration schema remains 1. Extraction and
253
+ Live use their dedicated native envelopes rather than a common result version.
236
254
 
237
255
  ## Extensions
238
256
 
239
257
  An extension has a unique printable `name` (maximum 100 characters), optional
240
- `prepareState(state, context)`, `prepareInput(input, context)`, and
258
+ `prepareState(state, context)`, `prepareInput(input, context)`,
259
+ `prepareConversation(items, context)`, and
241
260
  `onResult(result, context)`. Preparation extensions must support the request's
242
261
  input form; incompatible hooks fail explicitly before dispatch.
243
262
 
244
- Preparation returns replacement state or input, or `undefined` to leave it unchanged.
263
+ Preparation returns replacement state, ordered input or conversation, or
264
+ `undefined` to leave it unchanged.
245
265
  Preparation exceptions fail before inference with `EXTENSION_ERROR`; transformed
246
266
  content is revalidated, including capabilities and provider preflight. Preparation
247
267
  cannot rewrite question definitions or switch the model.
@@ -304,6 +324,8 @@ automatic model fallback. The CLI has a separate streaming JSONL implementation.
304
324
  - `createProvider(config)`: factory requiring an explicit `type` equal to `jev`,
305
325
  `openai-decisions`, or `cloudflare-decisions`.
306
326
  - `JevProvider`, `OpenAIDecisionsProvider`, `CloudflareDecisionsProvider`: native provider classes.
327
+ - `box(instructions)` and `JevProvider.createDecision(request, options?)`:
328
+ bounding boxes and native JSON assets; see the [Jev guide](../guides/JEV.md).
307
329
  - `scoreLevels(instructions, levels)` and the array overload of `choice`:
308
330
  labelled rubrics and typed string/boolean options, with provider capability checks.
309
331
  - `OpenAIDecisionsProvider.createDecision(request, options?)`: dedicated native
@@ -314,9 +336,9 @@ automatic model fallback. The CLI has a separate streaming JSONL implementation.
314
336
  an own `input.model` property or an explicitly supplied default; inherited
315
337
  models are ignored, and it never selects a Jev model.
316
338
  Question builders and schema generation validate without selecting an inference model.
317
- - `validateResult(input, questions, { native = false, probabilityDecimalPlaces })`: snapshot supplied
339
+ - `validateResult(input, questions, { native = false, nullableUsage = false, probabilityDecimalPlaces })`: snapshot supplied
318
340
  questions and validate the canonical result; native mode requires confidence
319
- and reported counts.
341
+ and reported counts unless the provider explicitly enables nullable usage.
320
342
  - `decideChoice(answer, { minProbability, minMargin = 0, abstainOptions = [], probabilityDecimalPlaces })`:
321
343
  returns a policy result, never executes an action. `minProbability` is required.
322
344
  - `DecisionError`, `publicError(error)`: public structured failure handling.
@@ -354,14 +376,14 @@ decision providers may preserve custom string labels. Generative prompt and
354
376
  normalization helpers independently validate the shared-state, legacy-question
355
377
  profile and explicitly reject unsupported request capabilities.
356
378
  Decision-kind classification alone does not require confidence or known counts;
357
- `validateResult({ native: true })` is a separate strict validation option used for
379
+ `validateResult(result, questions, { native: true })` is a separate strict validation option used for
358
380
  Jev's contract. See [custom decision](../guides/CUSTOM_DECISION.md),
359
381
  [custom generative](../guides/CUSTOM_GENERATIVE.md), and the shared
360
382
  [custom contract](../guides/CUSTOM_PROVIDERS.md).
361
383
 
362
384
  ## Validation guarantees
363
385
 
364
- Legacy choice criteria maps allow at most 255 options, matching Jev's contract. Noul
386
+ Legacy choice criteria maps must be nonempty. Noul
365
387
  criteria must be omitted or an object; its present true/false descriptions cannot
366
388
  be raw null. Score levels likewise cannot be raw null (nested null JSON values are
367
389
  still allowed). These invalid forms now fail before provider dispatch.
@@ -371,7 +393,10 @@ the answer. `evaluateBatch()` validates array holes as invalid requests before a
371
393
  item is dispatched, rather than spending on adjacent valid entries.
372
394
 
373
395
  The optional third argument to `validateResult()` must be a plain options object
374
- with optional boolean `native` and integer `probabilityDecimalPlaces` (1–15) fields.
396
+ with optional boolean `native`, boolean `nullableUsage`, and integer
397
+ `probabilityDecimalPlaces` (1–15) fields. Both booleans default to false.
398
+ `nullableUsage: true` permits null token counts when native validation is enabled;
399
+ it does not relax the native confidence requirement.
375
400
  Providers can declare a pure synchronous `getResultValidationOptions(model)` hook;
376
401
  its policy is validated before inference and is not read from response metadata.
377
402
  Returning a Promise is a configuration error; its rejection is observed without
@@ -392,3 +417,24 @@ Native provider cancellation/timeout errors preserve the last observed sanitized
392
417
  request ID and attempt count when available. The bridge may settle its own
393
418
  cancellation race before the provider returns diagnostics; do not require those
394
419
  optional fields on every `ABORTED` or `TIMEOUT` error.
420
+
421
+ ## Dedicated operations
422
+
423
+ Import these subpaths under `@fayegram-ai/decision-bridge/`. Neither is selected
424
+ by the root `createProvider` factory or implements the bridge's `evaluate` contract.
425
+
426
+ | Export | Runtime API | Result / integration |
427
+ | --- | --- | --- |
428
+ | `extraction` | `CloudflareExtractionProvider`, `validateExtractionRequest`, `extractBatch` | Immutable `{ extraction, meta }`, with native values and model/transport diagnostics; [extraction guide](../guides/EXTRACTION.md) |
429
+ | `live` | `createLiveDecisions`, `validateLiveConfiguration`, `validateLiveCommand`, `validateLiveEvent` | Commands, acknowledgements and event subscriptions over a caller-owned connection; [Live guide](../guides/LIVE_DECISIONS.md) |
430
+
431
+ Both export their request, result, configuration and transport types. Extraction
432
+ supports REST or a Workers AI binding; Live owns no authentication, audio,
433
+ connection establishment or reconnection. See the [CLI reference](CLI.md) for
434
+ their command namespaces.
435
+
436
+ Service quotas, maximum request counts, context/media limits and Gateway
437
+ admission remain authoritative upstream. Offline validation checks adapter
438
+ structure and client resource budgets. SDK and CLI do not estimate tokens or
439
+ enforce RPM/TPM. Explicit transport budgets can select up to 64 MiB, including
440
+ Jev and all Clef models; existing default budgets are retained.
@@ -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 |
@@ -18,6 +22,8 @@ reference does not imply that authenticated inference was run against its servic
18
22
  | OpenAI structured output modes | https://platform.openai.com/docs/guides/structured-outputs |
19
23
  | OpenAI native Decisions guide | https://developers.openai.com/api/docs/guides/decisions |
20
24
  | OpenAI Decisions request/response contract | https://developers.openai.com/api/reference/resources/decisions/methods/create |
25
+ | OpenAI Live Decisions guide | https://developers.openai.com/api/docs/guides/decisions-voice |
26
+ | OpenAI Live protocol | https://developers.openai.com/api/reference/resources/live/primary-websocket |
21
27
  | OpenRouter structured output and routing | https://openrouter.ai/docs/guides/features/structured-outputs |
22
28
  | Anthropic Messages, tools, usage, and stop reasons | https://platform.claude.com/docs/en/api/messages/create |
23
29
  | GitHub checkout action | https://github.com/actions/checkout |
@@ -44,6 +50,11 @@ separate operational check.
44
50
  - [Cloudflare-owned model implementation](https://huggingface.co/Cloudflare/clef-flash/blob/main/joint_schema_model.py), including output precision
45
51
  - [Cloudflare-owned Omni implementation](https://huggingface.co/Cloudflare/clef-omni/blob/main/joint_schema_model.py), including grouped media and output precision
46
52
 
53
+ ## Cloudflare extraction
54
+
55
+ - [Extraction concepts](https://developers.cloudflare.com/workers-ai/concepts/extraction-models/)
56
+ - [Clairic schema snapshot](https://github.com/cloudflare/cloudflare-docs/blob/a5cc2cba1a31b8bd95eda69dad151e6cd307ac7b/src/content/workers-ai-models/clairic.json), the pinned basis for established native fields; underspecified predictive JSON remains preserved
57
+
47
58
  ## Package and repository release
48
59
 
49
60
  - [Scoped public npm packages](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/)
@@ -0,0 +1,16 @@
1
+ // Explicit execution calls Cloudflare; configure account/token in your environment.
2
+ import { CloudflareExtractionProvider } from '../src/extraction/index.mjs';
3
+ import { publicError } from '../src/index.mjs';
4
+
5
+ try {
6
+ const provider = new CloudflareExtractionProvider();
7
+ const result = await provider.extract({
8
+ state: 'Invoice total: 1080 USD. Due date: 2026-11-01.',
9
+ reference_date: '2026-10-20',
10
+ fields: { amount: { type: 'number', unit: 'USD' }, due_date: { type: 'date' } },
11
+ });
12
+ console.log(JSON.stringify(result, null, 2));
13
+ } catch (error) {
14
+ console.error(publicError(error));
15
+ process.exitCode = 1;
16
+ }
@@ -0,0 +1,24 @@
1
+ // Call with your already authenticated, open WebSocket-like Live connection.
2
+ import { createLiveDecisions } from '../src/live/index.mjs';
3
+
4
+ export function attachLiveDecisions(connection) {
5
+ return createLiveDecisions({ transport: {
6
+ send(command) { connection.send(JSON.stringify(command)); },
7
+ subscribe(listener) {
8
+ const message = event => {
9
+ try { listener({ kind: 'event', event: JSON.parse(event.data) }); }
10
+ catch { listener({ kind: 'error' }); }
11
+ };
12
+ const close = () => listener({ kind: 'close' });
13
+ const error = () => listener({ kind: 'error' });
14
+ connection.addEventListener('message', message);
15
+ connection.addEventListener('close', close);
16
+ connection.addEventListener('error', error);
17
+ return () => {
18
+ connection.removeEventListener('message', message);
19
+ connection.removeEventListener('close', close);
20
+ connection.removeEventListener('error', error);
21
+ };
22
+ },
23
+ } });
24
+ }
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.3",
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",
@@ -44,6 +44,16 @@
44
44
  "browser": "./src/custom/generative/index.mjs",
45
45
  "import": "./src/custom/generative/index.mjs"
46
46
  },
47
+ "./extraction": {
48
+ "types": "./src/extraction/index.d.mts",
49
+ "browser": "./src/extraction/index.mjs",
50
+ "import": "./src/extraction/index.mjs"
51
+ },
52
+ "./live": {
53
+ "types": "./src/live/index.d.mts",
54
+ "browser": "./src/live/index.mjs",
55
+ "import": "./src/live/index.mjs"
56
+ },
47
57
  "./config.schema.json": "./schemas/decision.config.schema.json"
48
58
  },
49
59
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "title": "Decision Bridge configuration",
4
- "description": "Explicit schemaVersion 1 configuration. Runtime preflight also enforces URL, credential and provider limits.",
4
+ "description": "Explicit schemaVersion 1 configuration. Runtime validation checks adapter structure and client resource budgets; service limits remain authoritative upstream.",
5
5
  "oneOf": [
6
6
  {
7
7
  "type": "object",
@@ -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": 67108864
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
  },
@@ -393,6 +407,89 @@
393
407
  "provider",
394
408
  "module"
395
409
  ]
410
+ },
411
+ {
412
+ "type": "object",
413
+ "additionalProperties": false,
414
+ "properties": {
415
+ "$schema": {
416
+ "$ref": "#/$defs/text"
417
+ },
418
+ "schemaVersion": {
419
+ "const": 1
420
+ },
421
+ "model": {
422
+ "enum": [
423
+ "clairic",
424
+ "@cf/cloudflare/clairic"
425
+ ]
426
+ },
427
+ "apiKeyEnv": {
428
+ "$ref": "#/$defs/keyEnv"
429
+ },
430
+ "timeoutMs": {
431
+ "$ref": "#/$defs/timeout"
432
+ },
433
+ "baseURL": {
434
+ "$ref": "#/$defs/text"
435
+ },
436
+ "maxRetries": {
437
+ "$ref": "#/$defs/retries"
438
+ },
439
+ "maxRequestBytes": {
440
+ "$ref": "#/$defs/bytes"
441
+ },
442
+ "maxResponseBytes": {
443
+ "$ref": "#/$defs/bytes"
444
+ },
445
+ "provider": {
446
+ "const": "cloudflare-extraction"
447
+ },
448
+ "accountId": {
449
+ "$ref": "#/$defs/text"
450
+ },
451
+ "rejectIfBusy": {
452
+ "$ref": "#/$defs/boolean"
453
+ },
454
+ "gateway": {
455
+ "$ref": "#/$defs/gateway"
456
+ }
457
+ },
458
+ "required": [
459
+ "provider"
460
+ ],
461
+ "allOf": [
462
+ {
463
+ "if": {
464
+ "properties": {
465
+ "rejectIfBusy": {
466
+ "const": true
467
+ }
468
+ },
469
+ "required": [
470
+ "rejectIfBusy"
471
+ ]
472
+ },
473
+ "then": {
474
+ "properties": {
475
+ "maxRetries": {
476
+ "const": 0
477
+ },
478
+ "gateway": {
479
+ "properties": {
480
+ "retries": {
481
+ "properties": {
482
+ "maxAttempts": {
483
+ "const": 1
484
+ }
485
+ }
486
+ }
487
+ }
488
+ }
489
+ }
490
+ }
491
+ }
492
+ ]
396
493
  }
397
494
  ],
398
495
  "$defs": {
@@ -482,19 +579,16 @@
482
579
  "id": {
483
580
  "type": "string",
484
581
  "minLength": 1,
485
- "maxLength": 64,
486
582
  "pattern": "^[\\x20-\\x7e]*\\S[\\x20-\\x7e]*$"
487
583
  },
488
584
  "cacheKey": {
489
585
  "type": "string",
490
586
  "minLength": 1,
491
- "maxLength": 200,
492
587
  "pattern": "^[\\x20-\\x7e]*\\S[\\x20-\\x7e]*$"
493
588
  },
494
589
  "cacheTtl": {
495
590
  "type": "integer",
496
- "minimum": 60,
497
- "maximum": 2592000
591
+ "minimum": 0
498
592
  },
499
593
  "skipCache": {
500
594
  "type": "boolean"
@@ -509,7 +603,6 @@
509
603
  },
510
604
  "metadata": {
511
605
  "type": "object",
512
- "maxProperties": 5,
513
606
  "propertyNames": {
514
607
  "not": {
515
608
  "pattern": "^cf\\."
@@ -530,13 +623,11 @@
530
623
  "properties": {
531
624
  "maxAttempts": {
532
625
  "type": "integer",
533
- "minimum": 1,
534
- "maximum": 5
626
+ "minimum": 1
535
627
  },
536
628
  "retryDelayMs": {
537
629
  "type": "integer",
538
- "minimum": 0,
539
- "maximum": 60000
630
+ "minimum": 0
540
631
  },
541
632
  "backoff": {
542
633
  "enum": [