@fayegram-ai/decision-bridge 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/.env.example +10 -0
  2. package/CHANGELOG.md +75 -0
  3. package/CONTRIBUTING.md +57 -0
  4. package/LICENSE +21 -0
  5. package/README.md +307 -2
  6. package/SECURITY.md +88 -0
  7. package/bin/decision.mjs +15 -0
  8. package/docs/README.md +58 -0
  9. package/docs/development/ARCHITECTURE.md +118 -0
  10. package/docs/development/DESIGN.md +66 -0
  11. package/docs/development/README.md +12 -0
  12. package/docs/development/RELEASING.md +100 -0
  13. package/docs/development/REPOSITORY_LAYOUT.md +33 -0
  14. package/docs/development/TESTING.md +131 -0
  15. package/docs/guides/BROWSER.md +43 -0
  16. package/docs/guides/CLOUDFLARE_DECISIONS.md +374 -0
  17. package/docs/guides/CUSTOMIZATION.md +141 -0
  18. package/docs/guides/CUSTOM_DECISION.md +66 -0
  19. package/docs/guides/CUSTOM_GENERATIVE.md +164 -0
  20. package/docs/guides/CUSTOM_PROVIDERS.md +81 -0
  21. package/docs/guides/GETTING_STARTED.md +252 -0
  22. package/docs/guides/OPENAI_DECISIONS.md +210 -0
  23. package/docs/guides/PLATFORMS.md +107 -0
  24. package/docs/guides/SWITCHING_MODELS.md +202 -0
  25. package/docs/guides/TROUBLESHOOTING.md +82 -0
  26. package/docs/reference/CLI.md +202 -0
  27. package/docs/reference/CONFIGURATION.md +168 -0
  28. package/docs/reference/ERRORS.md +82 -0
  29. package/docs/reference/PROVIDERS.md +113 -0
  30. package/docs/reference/SDK.md +388 -0
  31. package/docs/reference/SOURCES.md +51 -0
  32. package/examples/basic.mjs +17 -0
  33. package/examples/batch.mjs +13 -0
  34. package/examples/cloudflare-decisions.mjs +22 -0
  35. package/examples/cloudflare-omni.mjs +32 -0
  36. package/examples/cloudflare-worker.mjs +21 -0
  37. package/examples/cloudflare.native.json +28 -0
  38. package/examples/configs/anthropic.json +9 -0
  39. package/examples/configs/cloudflare-decisions.json +8 -0
  40. package/examples/configs/custom-decision.json +9 -0
  41. package/examples/configs/custom-generative.json +14 -0
  42. package/examples/configs/jev.json +7 -0
  43. package/examples/configs/local.json +12 -0
  44. package/examples/configs/openai-decisions.json +7 -0
  45. package/examples/configs/openai.json +10 -0
  46. package/examples/configs/openrouter.json +10 -0
  47. package/examples/custom/decision-provider.mjs +15 -0
  48. package/examples/custom/generative-provider.mjs +13 -0
  49. package/examples/custom/openrouter.mjs +13 -0
  50. package/examples/customize.mjs +23 -0
  51. package/examples/decisions.native.json +19 -0
  52. package/examples/extended.mjs +43 -0
  53. package/examples/openai-decisions.mjs +20 -0
  54. package/examples/request.json +31 -0
  55. package/examples/requests.jsonl +2 -0
  56. package/package.json +87 -4
  57. package/schemas/decision.config.schema.json +553 -0
  58. package/src/async.mjs +63 -0
  59. package/src/batch.mjs +38 -0
  60. package/src/bridge.mjs +184 -0
  61. package/src/capabilities.mjs +95 -0
  62. package/src/cli/config-validation.mjs +89 -0
  63. package/src/cli/config.mjs +84 -0
  64. package/src/cli/decisions.mjs +19 -0
  65. package/src/cli/initialize.mjs +79 -0
  66. package/src/cli/io.mjs +171 -0
  67. package/src/cli/main.mjs +280 -0
  68. package/src/cli/ordered.mjs +43 -0
  69. package/src/cli/provider-loader.mjs +58 -0
  70. package/src/cloudflare-decisions.d.mts +78 -0
  71. package/src/custom/decision.d.mts +5 -0
  72. package/src/custom/decision.mjs +6 -0
  73. package/src/custom/definition.mjs +19 -0
  74. package/src/custom/generative/anthropic.mjs +75 -0
  75. package/src/custom/generative/index.d.mts +42 -0
  76. package/src/custom/generative/index.mjs +29 -0
  77. package/src/custom/generative/openai-compatible.mjs +101 -0
  78. package/src/custom/generative/schema.mjs +95 -0
  79. package/src/custom/generative/shared.mjs +39 -0
  80. package/src/decisions.d.mts +45 -0
  81. package/src/embedded.mjs +24 -0
  82. package/src/errors.mjs +49 -0
  83. package/src/index.d.mts +229 -0
  84. package/src/index.mjs +7 -0
  85. package/src/input.mjs +37 -0
  86. package/src/options.mjs +15 -0
  87. package/src/policy.mjs +44 -0
  88. package/src/probabilities.mjs +27 -0
  89. package/src/provider-contract.mjs +42 -0
  90. package/src/providers/cloudflare-audio.mjs +83 -0
  91. package/src/providers/cloudflare-codec.mjs +39 -0
  92. package/src/providers/cloudflare-decisions.mjs +81 -0
  93. package/src/providers/cloudflare-images.mjs +95 -0
  94. package/src/providers/cloudflare-media.mjs +36 -0
  95. package/src/providers/cloudflare-preflight.mjs +92 -0
  96. package/src/providers/cloudflare-profile.mjs +102 -0
  97. package/src/providers/cloudflare-transport.mjs +63 -0
  98. package/src/providers/cloudflare-validation.mjs +48 -0
  99. package/src/providers/cloudflare-video.mjs +84 -0
  100. package/src/providers/decisions-codec.mjs +61 -0
  101. package/src/providers/decisions-preflight.mjs +20 -0
  102. package/src/providers/decisions-validation.mjs +150 -0
  103. package/src/providers/index.mjs +26 -0
  104. package/src/providers/jev.mjs +51 -0
  105. package/src/providers/media-binary.mjs +51 -0
  106. package/src/providers/openai-decisions.mjs +51 -0
  107. package/src/questions.mjs +15 -0
  108. package/src/response-transport.mjs +176 -0
  109. package/src/runtime-env.mjs +4 -0
  110. package/src/transport.mjs +51 -0
  111. package/src/validation.mjs +245 -0
  112. package/src/version.mjs +2 -0
@@ -0,0 +1,202 @@
1
+ # Command runner reference
2
+
3
+ The optional command runner evaluates files using the same bridge available in
4
+ the SDK. For application integration, start with the [SDK guide](../guides/GETTING_STARTED.md).
5
+
6
+ After [installing the package](../guides/GETTING_STARTED.md#install-into-your-application)
7
+ in your project, run:
8
+
9
+ ```bash
10
+ npm exec -- decision-bridge --help
11
+ npm exec -- decision-bridge validate
12
+ ```
13
+
14
+ The examples below use `decision-bridge` as shorthand: prefix it with `npm exec --`
15
+ for a local installation, use it directly after a global installation, or replace
16
+ it with `node bin/decision.mjs` from a source checkout. `decision` and
17
+ `faye-decision` are equivalent aliases with the same commands and options.
18
+ `--help` and `--version` do not access credentials or services.
19
+
20
+ The CLI defaults to Jev. Flags override `DECISION_*` environment settings, which
21
+ override an explicit config file and then built-in defaults. It constructs an
22
+ explicit provider for the SDK; `createBridge` itself has no implicit selection.
23
+ Use the root README's [model list](../../README.md#supported-models) to select a
24
+ supported native model. Custom generative presets require an explicit model ID.
25
+
26
+ ## Commands
27
+
28
+ `--provider openai-decisions` selects OpenAI Decisions; `--provider cloudflare-decisions`
29
+ selects Clef. Add `--native`
30
+ for native request/response structures on `init`, `evaluate`, `validate`, or
31
+ `batch`; input mode is never inferred. Native calls require an explicit input file
32
+ or `-`. See [OpenAI Decisions](../guides/OPENAI_DECISIONS.md) and
33
+ [Cloudflare Clef](../guides/CLOUDFLARE_DECISIONS.md) for examples. Both providers report
34
+ model listing as unsupported.
35
+ Cloudflare CLI execution uses REST. The Workers AI binding is an SDK transport;
36
+ there is no CLI binding selector. Gateway settings route the selected REST request.
37
+
38
+ | Command | Behavior |
39
+ | --- | --- |
40
+ | `init [DIRECTORY]` | Create config and schema, sample request, environment template, and gitignore; generate a custom module when needed; never overwrite |
41
+ | `evaluate [FILE\|-]` | One evaluation; aliases `eval` and `ask` |
42
+ | `validate [FILE\|-]` | Validate input locally; does not call a provider |
43
+ | `schema [FILE\|-]` | Print the generative response schema for these questions; local only |
44
+ | `models` | List the configured provider's models; may be paginated |
45
+ | `batch FILE\|-` | Stream JSONL inputs and ordered JSONL result/error records |
46
+
47
+ No input filename on `evaluate`, `validate`, or `schema` selects the bundled sample,
48
+ not stdin. Use `-` explicitly for stdin. Empty invocation prints help. Configuration
49
+ and credential errors do not cause fallback to another backend.
50
+
51
+ ```bash
52
+ cat request.json | decision-bridge evaluate - --env-file .env --compact
53
+ decision-bridge evaluate request.json --env-file .env --output result.json
54
+ decision-bridge batch requests.jsonl --env-file .env --concurrency 4 --output results.jsonl
55
+ ```
56
+
57
+ On PowerShell, `Get-Content -Raw request.json | decision-bridge evaluate - --env-file .env`
58
+ is an alternative for stdin. File arguments avoid shell encoding differences.
59
+
60
+ ## Custom initialization
61
+
62
+ ```bash
63
+ decision-bridge init ./my-model --provider custom-generative --preset openrouter --model VENDOR/MODEL_ID
64
+ decision-bridge init ./my-service --provider custom-decision
65
+ decision-bridge init ./my-protocol --provider custom-generative
66
+ ```
67
+
68
+ Preset initialization writes a ready-to-configure profile and credential
69
+ placeholders. An unselected custom implementation writes `provider.mjs` with an
70
+ explicit service implementation error until you connect it. An existing
71
+ `--provider-module` is referenced without execution or overwrite. All profiles
72
+ include a copied JSON Schema for editor validation. See [custom providers](../guides/CUSTOM_PROVIDERS.md).
73
+
74
+ ## Flags
75
+
76
+ | Flag | Meaning |
77
+ | --- | --- |
78
+ | `--config FILE` | Explicit JSON configuration; never autodiscovered |
79
+ | `--env-file FILE` | Load an explicit environment file without changing the parent shell |
80
+ | `--provider NAME` | `jev`, `openai-decisions`, `cloudflare-decisions`, `custom-decision`, `custom-generative` |
81
+ | `--preset NAME` | custom-generative preset: `openai-compatible`, `openrouter`, or `anthropic`; exclusive with module |
82
+ | `--native` | Explicit native Decisions input/output; init/evaluate/validate/batch only |
83
+ | `--model ID` | Overrides input JSON model and provider default |
84
+ | `--base-url URL` | Custom endpoint; requires explicit credential selection |
85
+ | `--api-key-env NAME` | Environment variable name, never the key itself |
86
+ | `--account-id ID` | Cloudflare REST account ID |
87
+ | `--reject-if-busy`, `--no-reject-if-busy` | Explicit Cloudflare native-request override. Enabling fails local REST preflight with `UNSUPPORTED_CAPABILITY`; disabling permits ordinary inference. See the [REST limitation](../guides/CLOUDFLARE_DECISIONS.md#capacity-rejection-and-ai-gateway) |
88
+ | `--gateway-id ID` | Existing Cloudflare Gateway; richer settings in config |
89
+ | `--no-auth` | Only for a loopback OpenAI-compatible endpoint |
90
+ | `--timeout-ms N` | HTTP and bridge deadline for each evaluation/listing |
91
+ | `--retries N` | Additional eligible HTTP attempts; 0–10; native OpenAI/Cloudflare default to 0, Jev and generative presets to 2 |
92
+ | `--max-tokens N` | Generative output budget; default 4096 |
93
+ | `--response-format MODE` | `json_schema`, `json_object`, or `text`; compatible generative presets only |
94
+ | `--token-parameter NAME` | `max_completion_tokens` or `max_tokens` |
95
+ | `--provider-module FILE` | Explicit trusted ESM module; requires custom-decision or custom-generative |
96
+ | `--input FILE` | Alternative to positional request filename |
97
+ | `--output FILE`, `-o FILE` | Atomic file output; `-` means stdout |
98
+ | `--force` | Replace existing output; valid only with `--output` |
99
+ | `--concurrency N` | Batch in-flight limit 1–64; default 4 |
100
+ | `--pretty`, `--compact` | JSON formatting; pretty by default; batches always JSONL |
101
+ | `--help`, `-h`, `--version`, `-v` | Informational commands |
102
+
103
+ Unknown or duplicate flags fail. Provider-inapplicable flags fail instead of being
104
+ ignored. `--model` wins over request.model; otherwise request.model wins over the
105
+ configured model. See [configuration](CONFIGURATION.md) for full precedence.
106
+
107
+ ## JSON output contract
108
+
109
+ Single evaluation stdout is exactly the result object plus a newline. It is not
110
+ wrapped in a progress log or Markdown. Errors are `{ "error": { "code", "message",
111
+ ... } }` on stderr. No debug stacks or raw HTTP error bodies are printed.
112
+
113
+ Native Decisions output is `{ decision, meta }`. OpenAI refused questions fail the
114
+ complete evaluation, with allowlisted refusal diagnostics and validated usage.
115
+
116
+ Common results include `meta.providerKind` from the provider contract. Offline
117
+ `validate` requires no credentials and never imports custom modules. Its JSON
118
+ includes `validationScope: 'provider'` for known native/preset profiles or
119
+ `'structure'` for a custom module. Structure validation cannot verify an unknown
120
+ module's capabilities or protocol. `schema` is an offline generative-output tool;
121
+ it does not add generative support to a native provider.
122
+
123
+ A batch emits one compact record per nonblank input line:
124
+
125
+ ```text
126
+ {"line":1,"ok":true,"result":{...}}
127
+ {"line":2,"ok":false,"error":{"code":"VALIDATION_ERROR","message":"..."}}
128
+ ```
129
+
130
+ This is a schematic shape, not an inference example. `line` is the original
131
+ one-based physical line number, including blank lines in the numbering. Blank
132
+ lines produce no record. Malformed JSON on a single line is a failed record and
133
+ processing continues. Evaluation failures likewise remain explicit records.
134
+
135
+ The CLI streams input with a bounded, ordered window. A completed oldest result
136
+ is emitted even when stdin remains open and the producer has not sent another
137
+ line; the window does not have to fill before output begins. A slow earlier request can
138
+ hold back emission and dispatch even when later requests finish; this prioritizes
139
+ bounded memory and stable ordering over maximum throughput. Up to `concurrency`
140
+ requests may already be running when a failure is encountered. There is no CLI
141
+ `stopOnError` flag; the SDK array batch API provides that policy.
142
+
143
+ Invalid UTF-8, input-size violations, unreadable input, or interrupted execution are
144
+ global failures. Work already sent to a provider may have been billed. Do not assume
145
+ that a failed batch sent no requests: unlike the SDK array API, streaming CLI input
146
+ is not entirely prevalidated before execution.
147
+
148
+ ## Exit codes
149
+
150
+ | Code | Meaning |
151
+ | --- | --- |
152
+ | 0 | Success; also a closed downstream pipe (EPIPE) |
153
+ | 1 | Provider/evaluation failure or at least one failed batch record |
154
+ | 2 | Usage, input, configuration, or filesystem failure |
155
+ | 130 | Caller interruption/cancellation, including handled SIGINT/SIGTERM |
156
+
157
+ Both signals are handled as cancellation by this CLI; it intentionally does not
158
+ use 143 for SIGTERM. Cancellation also interrupts a blocked stdout write. The
159
+ POSIX subprocess SIGINT check is excluded on Windows, where console signal
160
+ delivery needs separate validation.
161
+
162
+ ## I/O and resource limits
163
+
164
+ Single JSON input: **8 MiB**. JSONL: **8 MiB per line, 64 MiB total**. Config:
165
+ **1 MiB**. Environment file: **64 KiB**. Provider request and response default to
166
+ 8 MiB each, configurable to at most 64 MiB. Cloudflare reads single JSON inputs
167
+ and individual JSONL records within 64 MiB, then applies model-specific serialized
168
+ request validation: Clef/Clef-flash default/cap 13 MiB; Omni default 36 MiB,
169
+ configurable through 64 MiB. Explicit smaller ceilings remain in force. Request
170
+ model overrides and mixed-model JSONL batches use each effective model's budget.
171
+ The 64 MiB JSONL total remains. These are client limits, not a promise
172
+ that every remote model has an equivalent context window.
173
+
174
+ Per-line limits exclude LF or CRLF delimiters, regardless of stream chunking.
175
+ A final record may omit its newline; its complete bytes must still fit the limit.
176
+ The total input limit includes delimiters.
177
+
178
+ Output files are written to a same-directory exclusive temporary file with mode
179
+ 0600, synced, then committed. Existing paths are protected unless `--force` is
180
+ explicit. A batch containing ordinary failed records still produces a complete
181
+ JSONL file and exits 1. A global failure removes the unfinished temporary output.
182
+ Stdout cannot be rolled back and may contain a valid prefix after interruption.
183
+ No parent output directories are created automatically. Filesystems without hard
184
+ link support can reject no-clobber finalization; the error is explicit. File mode
185
+ and atomic filesystem semantics remain operating-system/filesystem dependent.
186
+
187
+ ## Input and cancellation boundaries
188
+
189
+ String-valued flags cannot be empty or whitespace-only, including `--config`,
190
+ `--env-file`, `--input` and `--output`. Such values return `USAGE_ERROR` before
191
+ configuration loading or paid calls, rather than selecting an unintended default.
192
+ Valid paths with embedded spaces remain supported.
193
+
194
+ The input/output check resolves existing paths and file identities, so directory
195
+ symlinks/junctions and hard-link aliases cannot bypass the input-overwrite guard.
196
+ This is an accidental-overwrite safeguard, not a filesystem sandbox against
197
+ another process racing to change paths.
198
+
199
+ Already-cancelled CLI work does not initialize a project or create an output file.
200
+ File writers check cancellation before writes and before submitting the atomic
201
+ commit. A commit already submitted to the operating system cannot be undone;
202
+ cancellation does not roll back a completed write or remote inference billing.
@@ -0,0 +1,168 @@
1
+ # Configuration and credentials
2
+
3
+ ## Explicit CLI JSON configuration
4
+
5
+ ```json
6
+ {
7
+ "$schema": "./decision.config.schema.json",
8
+ "schemaVersion": 1,
9
+ "provider": "jev",
10
+ "model": "jev-latest",
11
+ "apiKeyEnv": "TYPESAFE_API_KEY",
12
+ "timeoutMs": 30000,
13
+ "maxRetries": 2
14
+ }
15
+ ```
16
+
17
+ Load it with `--config path/to/decision.config.json`. The CLI never scans a working
18
+ directory for executable configuration. It does not read `.env` implicitly; use
19
+ `--env-file .env`. Runtime SDK configuration uses constructor options instead.
20
+
21
+ Jev is the CLI's built-in provider default. In SDK code, `createBridge` requires
22
+ an explicit `provider` or `jev` selection, and `createProvider` requires `type`.
23
+ Provider-specific model defaults apply after that selection. Root SDK selection
24
+ is native-only. Generative presets use `createGenerativeProvider` from the
25
+ `custom/generative` subpath. CLI custom modules declare their kind explicitly.
26
+
27
+ `init` copies the configuration JSON Schema beside the generated config and adds
28
+ `$schema` for editor completion. The packaged schema is available at
29
+ `@fayegram-ai/decision-bridge/config.schema.json` and in
30
+ [schemas/decision.config.schema.json](../../schemas/decision.config.schema.json).
31
+ It checks profile structure; runtime preflight additionally checks endpoint,
32
+ credential, capability, and model-specific limits.
33
+ Cloudflare CLI configuration always selects REST. Inject a Workers AI binding
34
+ through the SDK's `CloudflareDecisionsProvider` constructor instead of a JSON
35
+ profile. Gateway configuration is optional for either SDK transport and for CLI REST.
36
+
37
+ | Field | Values / scope |
38
+ | --- | --- |
39
+ | `$schema` | Optional nonempty schema reference for editors |
40
+ | `schemaVersion` | 1; optional, but generated profiles include it |
41
+ | `provider` | `jev` default, `openai-decisions`, `cloudflare-decisions`, `custom-decision`, `custom-generative` |
42
+ | `preset` | `custom-generative` only: `openai-compatible`, `openrouter`, `anthropic`; mutually exclusive with module |
43
+ | `model` | Nonempty model ID; built-in native providers have defaults; required for generative evaluation unless the request supplies it |
44
+ | `baseURL` | HTTPS API base; loopback may use HTTP |
45
+ | `apiKeyEnv` | Credential environment variable name; inline `apiKey` is rejected |
46
+ | `noAuth` | Boolean; true only for loopback OpenAI-compatible endpoints |
47
+ | `timeoutMs` | Positive integer up to 2,147,483,647 milliseconds |
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 |
50
+ | `accountId` | Cloudflare REST account ID; otherwise `CLOUDFLARE_ACCOUNT_ID` |
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
+ | `gateway` | Cloudflare Gateway object; see the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md) |
53
+ | `maxTokens` | Generative output budget, 1–1,048,576; default 4096 |
54
+ | `responseFormat` | Compatible generative presets: `json_schema`, `json_object`, `text` |
55
+ | `tokenParameter` | Compatible generative presets: `max_completion_tokens` or `max_tokens` |
56
+ | `parameters` | Extra JSON generation parameters, excluding bridge-controlled fields |
57
+ | `module` | Required for custom-decision; alternative to preset for custom-generative; explicit local ESM file |
58
+ | `options` | Custom factory's JSON options, e.g. its own baseURL |
59
+
60
+ Upper numeric limits validate client configuration, not vendor model capabilities.
61
+ Cloudflare selects its byte budget from the effective request model, including
62
+ per-call overrides. An explicit smaller `maxRequestBytes` remains the ceiling;
63
+ selecting Omni cannot enlarge it. Omni's default is a bridge policy, not a
64
+ documented whole-request service guarantee.
65
+ Provider-specific unused fields are rejected. In a module profile, transport and
66
+ generation settings belong inside `options`; the custom factory owns them. Top-level
67
+ `timeoutMs` still bounds the bridge. The module may implement its own networking.
68
+
69
+ `parameters` cannot override model, messages, system instructions, schemas, tools,
70
+ streaming, token-limit fields, or credentials. Legacy `functions` and
71
+ `function_call` controls are also reserved. Unrecognized model-specific
72
+ parameters may be rejected by the server; they are not silently dropped by us.
73
+
74
+ ## Precedence
75
+
76
+ For provider/configuration settings:
77
+
78
+ ```text
79
+ CLI flags > DECISION_* environment variables > explicit JSON file > defaults
80
+ ```
81
+
82
+ For credentials loaded from an explicit environment file:
83
+
84
+ ```text
85
+ existing process environment > --env-file values
86
+ ```
87
+
88
+ The loader does not mutate the process environment. The SDK independently reads
89
+ its documented ambient credential variables at provider construction.
90
+
91
+ The [supported-model list](../../README.md#supported-models) records native
92
+ selectors, aliases and defaults. Configuration precedence does not expand a
93
+ provider's accepted models or grant account access.
94
+
95
+ For evaluation model selection:
96
+
97
+ ```text
98
+ --model > request.model > resolved config/environment model > provider default
99
+ ```
100
+
101
+ Jev defaults to `jev-latest`, with `JEV_MODEL` as a legacy/direct convenience.
102
+ Native OpenAI Decisions defaults to `gpt-6-luna`; it accepts transport settings
103
+ and rejects generative output settings. Its default retry count is zero. Cloudflare Clef defaults to `clef`, uses
104
+ account/token credentials, rejects generation settings, and also defaults to zero retries.
105
+ A `DECISION_MODEL` or file model takes precedence over that convenience.
106
+ Custom generative presets and modules do not inherit a
107
+ Jev model. Offline validation of a generative/module request without a model reports
108
+ `model: null`; evaluation requires a request model or provider default.
109
+
110
+ Each layer is merged separately. Changing provider, preset, or module at a layer
111
+ boundary discards all lower-layer implementation settings: endpoint, key selector,
112
+ model, generation parameters, transport retries/byte limits, preset/module/options,
113
+ and Cloudflare account/busy/Gateway settings. Only `timeoutMs` is shared. This
114
+ prevents stale credentials and protocol options crossing implementation boundaries.
115
+ Same-implementation overrides retain compatible settings. Supply new settings
116
+ explicitly when switching. A module-to-preset or preset-to-module switch clears
117
+ the old selector. Module identity uses the resolved file path: a file-relative
118
+ module and a working-directory-relative flag retain settings only when both
119
+ identify the same file.
120
+
121
+ ## Recognized environment variables
122
+
123
+ | Variable | Purpose |
124
+ | --- | --- |
125
+ | `TYPESAFE_API_KEY` | Native Jev credential |
126
+ | `OPENAI_API_KEY` | Native Decisions and OpenAI-compatible credential at the OpenAI origin |
127
+ | `OPENROUTER_API_KEY` | OpenRouter credential |
128
+ | `ANTHROPIC_API_KEY` | Anthropic credential |
129
+ | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare REST account ID |
130
+ | `CLOUDFLARE_API_TOKEN` | Cloudflare REST token at the official origin |
131
+ | `DECISION_ACCOUNT_ID` | CLI Cloudflare account selection |
132
+ | `DECISION_REJECT_IF_BUSY` | CLI Cloudflare busy mode; literal `true` or `false` |
133
+ | `DECISION_GATEWAY_ID` | CLI Cloudflare Gateway ID |
134
+ | `JEV_MODEL` | Native Jev default model convenience |
135
+ | `DECISION_PROVIDER` | CLI provider selection |
136
+ | `DECISION_PRESET` | CLI custom-generative preset selection |
137
+ | `DECISION_MODEL` | CLI configured model |
138
+ | `DECISION_BASE_URL` | CLI explicit endpoint |
139
+ | `DECISION_API_KEY_ENV` | CLI credential variable selector |
140
+ | `DECISION_TIMEOUT_MS` | CLI timeout, decimal integer |
141
+ | `DECISION_MAX_RETRIES` | CLI retry count, decimal integer |
142
+ | `DECISION_RESPONSE_FORMAT` | CLI compatible output mode |
143
+
144
+ `RUN_LIVE_TESTS=1` applies only to the explicit development smoke helper.
145
+ No background access or live testing is enabled by normal test commands.
146
+
147
+ ## Custom endpoint credential boundary
148
+
149
+ A CLI `baseURL` always requires explicit `apiKeyEnv`, even when it happens to equal
150
+ a preset origin, or `noAuth: true` for loopback compatible servers. This makes the
151
+ credential destination a deliberate selection. OpenRouter's preset origin cannot
152
+ be changed; use the `openai-compatible` custom-generative preset for a different gateway.
153
+
154
+ In SDK constructors, an ambient key is used only for the provider's original
155
+ origin. A caller-supplied third-party origin requires an explicit `apiKey`.
156
+ `apiKey: null` disables authentication only for OpenAI-compatible loopback URLs.
157
+ Credentials in URL userinfo, query strings, or fragments are rejected. Redirects
158
+ are not followed. These protections are not an SSRF sandbox: applications that
159
+ accept untrusted endpoint configuration must enforce their own destination policy.
160
+
161
+ ## Profiles
162
+
163
+ See [examples/configs/](../../examples/configs/) for native profiles, generative
164
+ presets, and separate custom decision/generative modules. Use
165
+ `--provider custom-generative --preset NAME` for a preset, or select a custom kind
166
+ with `--provider-module FILE`. The module flag does not infer classification. Replace placeholder model identifiers
167
+ with IDs returned by the intended account/server. Profiles intentionally contain
168
+ no real key or claim that one model is available to every user.
@@ -0,0 +1,82 @@
1
+ # Errors and recovery
2
+
3
+ `DecisionError` extends `Error` and exposes `code`, optional `status`, `requestId`,
4
+ `gatewayLogId`, `attempts`, and numeric `upstreamCode` for Cloudflare diagnostics.
5
+ OpenAI Decisions refusal errors can also carry validated `refusedQuestions` and
6
+ `usage`. `toJSON()` and `publicError(error)` return safe structured details.
7
+ Unknown exceptions become a generic message, not a copied stack/body. Custom
8
+ providers deliberately constructing `DecisionError` must keep its message safe.
9
+
10
+ | Code | Meaning and response |
11
+ | --- | --- |
12
+ | `DECISION_ERROR` | Generic code for a deliberately constructed DecisionError without a more specific code |
13
+ | `CONFIGURATION_ERROR` | Invalid/unknown option, missing credential, incompatible provider setting, or invalid endpoint; fix configuration |
14
+ | `VALIDATION_ERROR` | Invalid request/template/JSON; fix input before retrying |
15
+ | `UNSUPPORTED_CAPABILITY` | Selected provider/model or preparation extension cannot support this request; fails before dispatch |
16
+ | `USAGE_ERROR` | Invalid CLI flags/arguments or stdin usage; consult help |
17
+ | `IO_ERROR` | Input/output path or permissions failure; preserve previous output |
18
+ | `INPUT_TOO_LARGE` | CLI byte limit exceeded; reduce or split input |
19
+ | `REQUEST_TOO_LARGE` | Encoded provider request exceeds configured limit; reduce input/schema |
20
+ | `RESPONSE_TOO_LARGE` | Provider response exceeds configured limit; inspect budget/model behavior |
21
+ | `HTTP_ERROR` | Non-success HTTP response; use status/request ID, not raw error body |
22
+ | `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 |
26
+ | `UPSTREAM_TIMEOUT`, `UPSTREAM_ABORTED` | Cloudflare upstream timeout (3007) or abort (3008) |
27
+ | `NETWORK_ERROR` | Transport failed ambiguously; no automatic network retry occurred |
28
+ | `RETRY_BUDGET_EXCEEDED` | Server delay cannot fit remaining deadline; choose a later attempt explicitly |
29
+ | `TIMEOUT` | Provider, bridge, preparation, or listing deadline exceeded |
30
+ | `ABORTED` | Caller cancelled; remote work might already have been billed |
31
+ | `INVALID_RESPONSE` | Non-byte response chunks, wrong envelope, or invalid UTF-8/JSON/schema/distribution/usage; do not trust partial answers |
32
+ | `MODEL_REFUSAL` | Provider explicitly declined the evaluation; no fabricated result |
33
+ | `TRUNCATED_OUTPUT` | Output/context budget exhausted; revise budget/input explicitly |
34
+ | `EXTENSION_ERROR` | Preparation failed; inference was not issued after that failure |
35
+ | `PROVIDER_ERROR` | Sanitized custom-provider exception |
36
+ | `UNSUPPORTED_OPERATION` | Provider does not implement listing or another requested operation |
37
+ | `BATCH_SKIPPED` | SDK stop-on-error prevented a not-yet-started record |
38
+ | `UNEXPECTED_ERROR` | Unrecognized error sanitized for public output |
39
+
40
+ ## Retry policy
41
+
42
+ OpenAI Decisions refusals include `refusedQuestions` and validated `usage`, plus
43
+ available HTTP diagnostics. A refusal of any question fails that complete call;
44
+ successful siblings are not exposed as a partial result.
45
+
46
+ The shared response transport retries statuses **429, 502, 503, 504, and 529**
47
+ only, for both HTTP requests and Workers AI binding responses. `maxRetries`
48
+ 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
50
+ request. All
51
+ attempts, backoff, and response-body reading share one timeout budget.
52
+
53
+ `Retry-After` seconds or HTTP dates are honored. Without it, exponential backoff
54
+ starts at 250 ms with jitter and an 8-second base cap. A delay that cannot fit the
55
+ remaining budget fails instead of violating the server's requested wait.
56
+
57
+ Authentication failures, generic HTTP 500, invalid output, model refusal, and
58
+ ambiguous network failures are not retried automatically. HTTP retries are not an
59
+ exactly-once billing guarantee. Set `maxRetries: 0` when your workflow requires
60
+ manual retry decisions. The bridge never retries a successful request because an
61
+ observer failed; inspect `meta.observerFailures` separately.
62
+
63
+ ## Practical diagnosis
64
+
65
+ 401/403 usually requires checking key/account/model authorization with the provider;
66
+ 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
69
+ may still emit invalid probabilities or be routed through an incompatible endpoint.
70
+ Change configuration deliberately and retest; do not disable local validation.
71
+
72
+ Enabling busy execution options for Cloudflare Clef REST fails local preflight
73
+ with `UNSUPPORTED_CAPABILITY`, without an HTTP status, upstream code, or dispatch.
74
+ Direct calls to the hosted service were observed to reject that field with
75
+ HTTP 422 and `upstreamCode: 5012`, distinct from capacity code 3040. Other upstream
76
+ validation failures can still return HTTP 422/5012 and retain safe diagnostics.
77
+ Keep the default disabled setting for ordinary REST
78
+ inference; see the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md#capacity-rejection-and-ai-gateway).
79
+
80
+ Keep a request ID and available Gateway log ID, bridge version, configured provider, requested/reported model,
81
+ and template revision for incident analysis. Log input or results only under your
82
+ own data-handling policy. No logging or telemetry is enabled by default.
@@ -0,0 +1,113 @@
1
+ # Provider contracts and compatibility
2
+
3
+ ## Native routes
4
+
5
+ | Provider type | Wire API | Credential | Default model | Output strategy |
6
+ | --- | --- | --- | --- | --- |
7
+ | `jev` | `POST /v1/systemone` at api.typesafe.ai | `TYPESAFE_API_KEY` | `jev-latest` | Native typed decisions |
8
+ | `openai-decisions` | `POST /v1/decisions` at api.openai.com | `OPENAI_API_KEY` | `gpt-6-luna` | Native Decisions with common and dedicated access |
9
+ | `cloudflare-decisions` | Workers AI REST model route or `AI.run` binding | `CLOUDFLARE_API_TOKEN` and account ID for REST | `clef` | Native Clef decisions with common and dedicated access |
10
+
11
+ `createProvider()` and the root SDK export these three native providers only.
12
+ Custom decision services use `defineDecisionProvider` from `custom/decision`.
13
+ General-purpose adapters are opt-in presets in `custom/generative`; they are
14
+ maintained, but are not native decision models. See [custom providers](../guides/CUSTOM_PROVIDERS.md).
15
+
16
+ ## Supported model selection
17
+
18
+ See the root README's [model list](../../README.md#supported-models) for the
19
+ built-in selectors and aliases. Jev accepts `jev-latest` and explicit Jev IDs from
20
+ its account catalog. OpenAI Decisions supports only `gpt-6-luna` in this release.
21
+ Cloudflare supports `clef`, `clef-flash`, and `clef-omni`, including each full
22
+ `@cf/cloudflare/<model>` alias. Unsupported OpenAI/Cloudflare selectors fail
23
+ local preflight with `UNSUPPORTED_CAPABILITY`.
24
+
25
+ Custom generative presets require an explicit available model and have no default.
26
+ Custom decision or generative modules define their own model/capability contract.
27
+ Local model support is separate from account permission and hosted availability.
28
+
29
+ The interface is model-neutral, not a guarantee that every model or endpoint
30
+ supports every strategy. A compatible URL alone is insufficient: the chosen model
31
+ must follow the decision instruction, fit the input/schema, and return valid data.
32
+ Provider behavior is covered by local protocol tests and checked against official
33
+ specifications. These checks do not establish compatibility with every account or
34
+ model. Run the optional [live checks](../development/TESTING.md) for the provider,
35
+ account, model, and transport you intend to use; see the protocol [sources](SOURCES.md).
36
+
37
+ ## Common request capabilities
38
+
39
+ All native providers and custom generative presets accept the legacy shared-state and `choice` / `noul` / `score`
40
+ contract. The following additional forms are checked before dispatch:
41
+
42
+ | Provider | Ordered text / embedded images | Typed string/boolean choices | Labelled score levels | Safety identifier | Model listing |
43
+ | --- | --- | --- | --- | --- | --- |
44
+ | Jev | Unsupported | Unsupported | Unsupported | Unsupported | Supported |
45
+ | OpenAI Decisions | Supported; up to 128 images, preserving order | Supported | Supported | Supported | Unsupported |
46
+ | Cloudflare Clef / Clef-flash | Supported; up to four images before text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
47
+ | Cloudflare Clef-omni | Supported; up to four images before audio/video/text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
48
+ | OpenAI-compatible / OpenRouter / Anthropic | Unsupported | Unsupported | Unsupported | Unsupported | Supported; service-specific catalog |
49
+
50
+ `bridge.getCapabilities(model)` reports the local profile, and
51
+ `bridge.validate(request)` checks that profile and provider preflight without
52
+ inference. These methods do not authenticate an account or query availability.
53
+ Legacy choice maps and score rubrics have their own limits, described in the
54
+ [SDK reference](SDK.md#question-builders). Custom providers declare additional
55
+ capabilities explicitly.
56
+
57
+ Omni additionally accepts up to four audio clips and two videos. The optional
58
+ capability fields `audio`, `videos`, `maxAudioClips`, and `maxVideos` normalize to
59
+ false/zero when omitted, preserving legacy custom profiles. Cloudflare's older
60
+ models and all other shipped providers reject audio/video before inference.
61
+
62
+ Dedicated `createDecision()` access is available on OpenAI Decisions and
63
+ Cloudflare providers. It uses each service's native request/response shape and
64
+ bypasses bridge preparation and observation hooks.
65
+
66
+ ## Native Jev
67
+
68
+ The request directly contains `model`, shared `state`, and `questions`. Jev's
69
+ `choice`, `noul`, and `score` results are preserved and strictly validated. No
70
+ intermediate language model, skill, portal prompt, or chat conversion is used.
71
+ `GET /v1/models` lists native models. A listed catalog can differ from valid aliases;
72
+ `jev-latest` is the documented stable alias.
73
+
74
+ Pin a version after evaluating it for your workload rather than assuming a moving
75
+ alias yields reproducible quality. Store the returned `model` along with the
76
+ requested model and your question-template revision.
77
+
78
+ ## Native OpenAI Decisions
79
+
80
+ For the native Decisions endpoint, use `openai-decisions`; its capabilities,
81
+ refusal policy, image input, and dedicated API are documented in the
82
+ [OpenAI Decisions guide](../guides/OPENAI_DECISIONS.md). Native decision providers
83
+ report native probabilities, with provider-specific confidence semantics.
84
+
85
+ ## Native Cloudflare Clef
86
+
87
+ Use `cloudflare-decisions` for Clef, Clef-flash, and Clef-omni through REST or an injected
88
+ Workers AI binding. Common and native access share validation and codecs. See the
89
+ [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md) for image/audio/video limits, structured
90
+ content, typed values, rounding, capacity rejection, Gateway, and CLI examples.
91
+
92
+ ## Native versus elicited probabilities
93
+
94
+ Jev, OpenAI Decisions, and Clef report native outputs and confidence. Generative models are prompted
95
+ to produce probability distributions. Those estimates are not logprobs, repeated
96
+ sampling frequencies, calibrated confidence, or proof of correctness.
97
+
98
+ Generative providers derive an argmax choice and a probability-weighted score from the
99
+ elicited distributions. They do not synthesize native confidence statistics. Both
100
+ paths use the same application contract, but metadata records their provenance:
101
+ `native` versus `elicited`. `meta.providerKind` separately records `decision` or
102
+ `generative` from the provider contract. Classification does not force Jev's
103
+ confidence/usage requirements on a custom decision service. Evaluate performance and calibration on representative
104
+ labeled data before adopting thresholds or comparing costs/latencies.
105
+
106
+ ## Compatibility boundaries
107
+
108
+ Remote context limits, JSON Schema limits, model availability, output budgets,
109
+ pricing, and rate limits are provider/account specific. The bridge's local caps
110
+ do not override them. This release has no silent provider failover, model search,
111
+ credential refresh, provider-side batch API, or universal quality guarantee.
112
+ Unsupported protocols require an explicit custom decision or generative provider; unsupported server
113
+ features require an explicitly supported mode or a different model.