lua-cli 3.49.0 → 3.49.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.
@@ -619,7 +619,8 @@ lua decisions [command] [name] [version] [options]
619
619
  - `--force` - Skip the confirmation (`publish`, `delete`)
620
620
  - `--state <json>` - `run`: the state to decide on
621
621
  - `--message <text>` - `run`: sent as `{ message: <text> }`; an empty or blank message is a usage error (exit 2)
622
- - `--provider <jev|openai|auto>` - `run`: override the version's provider
622
+ - `--provider <jev|openai|openrouter|auto>` - `run`: override the version's provider
623
+ - `--model <id>` - `run`: override the version's model; an OpenRouter id such as `liquid/d1` runs on OpenRouter (see [Decisions](api/Decisions.md#other-decision-models-through-openrouter))
623
624
  - `--min-confidence <n>` - `run`: override the version's `minConfidence` (0 to 1)
624
625
 
625
626
  **Example:**
package/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # lua-cli v3.49.0
1
+ # lua-cli v3.49.2
2
2
 
3
- Welcome to the comprehensive API documentation for lua-cli v3.49.0. This guide covers every API, class, and function exported by the package.
3
+ Welcome to the comprehensive API documentation for lua-cli v3.49.2. This guide covers every API, class, and function exported by the package.
4
4
 
5
5
  ## 📚 Documentation Index
6
6
 
@@ -263,7 +263,7 @@ Each API doc includes:
263
263
 
264
264
  ---
265
265
 
266
- **Version:** 3.49.0
266
+ **Version:** 3.49.2
267
267
  **Last Updated:** October 2026
268
268
 
269
269
  ---
@@ -120,13 +120,13 @@ Question names and option labels are at most 64 characters. Any name works, incl
120
120
  interface DecisionResult {
121
121
  decisionId: string;
122
122
  answers: { [questionName: string]: DecisionAnswer };
123
- provider: 'jev' | 'openai';
123
+ provider: 'jev' | 'openai' | 'openrouter';
124
124
  model: string;
125
125
  rule: 'pinned' | 'images' | 'region' | 'zero_retention' | 'default' | 'fallback';
126
126
  usage: { inputTokens: number | null };
127
127
  durationMs: number;
128
128
  cached: boolean;
129
- fallback?: { from: 'jev' | 'openai'; reason: string };
129
+ fallback?: { from: 'jev' | 'openai' | 'openrouter'; reason: string };
130
130
  }
131
131
  ```
132
132
 
@@ -254,20 +254,56 @@ Under `lua test` and through the HTTP API, `decision_timeout`, `decision_unavail
254
254
 
255
255
  ## Options
256
256
 
257
- | Option | Values | Default | Description |
258
- | --------------- | ------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
259
- | `provider` | `'auto'`, `'jev'`, `'openai'` | `'auto'` | Which provider answers, where `auto` lets the platform pick. |
260
- | `model` | a model id of that provider | provider's | The exact model to use, which must belong to the chosen provider. |
261
- | `fallback` | `'jev'`, `'openai'`, `'none'` | other one | The provider to try when the first one fails, or `none` to never fall back, and a pinned `provider` never falls back unless you set this. |
262
- | `region` | `'eu'` | none | Routes the call to OpenAI's EU endpoint. |
263
- | `zeroRetention` | `true`, `false` | `false` | Routes the call to OpenAI only, and no retention flag is sent with the request. |
264
- | `minConfidence` | 0 to 1 | `0.7` | Below this confidence a choice or score answer carries `uncertain: true`. |
265
- | `timeoutMs` | 100 to 15000 | `3000` | How long the whole call may take, fallback included, before it fails with a timeout, and a value outside the range is clamped to it. |
266
- | `cache` | `{ ttlSeconds }` | none | Reuses the answer to an identical request for this many seconds. |
267
- | `logState` | `'summary'`, `'full'`, `'none'` | `'summary'` | How much of the state is kept in the decision log. |
257
+ | Option | Values | Default | Description |
258
+ | --------------- | --------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
259
+ | `provider` | `'auto'`, `'jev'`, `'openai'`, `'openrouter'` | `'auto'` | Which provider answers, where `auto` lets the platform pick between Jev and OpenAI. `openrouter` needs a `model`. |
260
+ | `model` | a model id of that provider | provider's | The exact model to use. `jev-*` runs on Jev, `gpt-*` on OpenAI, and a `vendor/model` id runs on OpenRouter (see below). |
261
+ | `fallback` | `'jev'`, `'openai'`, `'none'` | other one | The provider to try when the first one fails, or `none` to never fall back, and a pinned `provider` never falls back unless you set this. |
262
+ | `region` | `'eu'` | none | Routes the call to OpenAI's EU endpoint. |
263
+ | `zeroRetention` | `true`, `false` | `false` | Routes the call to OpenAI only, and no retention flag is sent with the request. |
264
+ | `minConfidence` | 0 to 1 | `0.7` | Below this confidence a choice or score answer carries `uncertain: true`. |
265
+ | `timeoutMs` | 100 to 15000 | `3000` | How long the whole call may take, fallback included, before it fails with a timeout, and a value outside the range is clamped to it. |
266
+ | `cache` | `{ ttlSeconds }` | none | Reuses the answer to an identical request for this many seconds. |
267
+ | `logState` | `'summary'`, `'full'`, `'none'` | `'summary'` | How much of the state is kept in the decision log. |
268
268
 
269
269
  A pinned `provider` overrides the images, `region` and `zeroRetention` rules.
270
270
 
271
+ ### Other decision models, through OpenRouter
272
+
273
+ Beyond Jev and OpenAI, you can run a decision on any of the models below by setting `model` to its id. These models are served through OpenRouter, so the result reports `provider: 'openrouter'`.
274
+
275
+ ```typescript
276
+ const r = await Decisions.ask({
277
+ state: { message: input.text },
278
+ questions: { refund: Decisions.odds('Is the customer asking for a refund?') },
279
+ model: 'liquid/d1',
280
+ });
281
+ ```
282
+
283
+ Requests and answers have the same shape for every model, and the result's `model` names the exact model version that answered. `auto` never picks one of these models, and `fallback` cannot name one. A call on one of them falls back to Jev or OpenAI when you set `fallback`. Jev and OpenAI are not reached this way: `typesafe/...` and `openai/...` ids fail with `decision_invalid`, so use `provider: 'jev'` or `'openai'`. Any other `vendor/model` id is accepted locally and checked when the call is made: a misspelt or unknown id fails with `decision_rejected` and the message names it, so check `model` against the list below before you ship.
284
+
285
+ Available models, October 2026:
286
+
287
+ | Model id | Input | Context | Typical latency\* | Notes |
288
+ | --------------------------------------- | ------------ | ------- | ----------------- | ---------------------------------------------------------------------- |
289
+ | `perplexity/pplx-decider-v1.1-27b` | text, images | 262k | 240 ms | Recommended |
290
+ | `liquid/d1` | text | 65k | 270 ms | Recommended |
291
+ | `microsoft/microsoft-decision-1` | text | 32k | 280 ms | Recommended |
292
+ | `cloudflare/clef-flash` | text, images | 65k | 330 ms | Recommended |
293
+ | `inception/mercury-decide` | text | 65k | 340 ms | Recommended |
294
+ | `togethercomputer/tev1-4b-experimental` | text | 32k | 270 ms | Recommended. At most 20 options per question and 32 questions per call |
295
+ | `cloudflare/clef` | text, images | 65k | 360 ms | Recommended |
296
+ | `upstage/solar-decide-flash` | text | 524k | 1 s | At most 26 options per question |
297
+ | `upstage/solar-decide` | text | 524k | 1 to 7 s | At most 26 options per question. Often slower than the default timeout |
298
+ | `jaredpalmer/kev-4b` | text | 8k | 1.6 s | |
299
+ | `nace-ai/drex-v1.5` | text | 131k | 480 ms | |
300
+
301
+ \* Median response time in Lua's internal benchmarks, October 2026. Jev answers in about 230 ms on the same calls.
302
+
303
+ Not available, because they keep request data and Lua only runs decisions on models that do not: `openai/gpt-6-luna-decisions` (use `provider: 'openai'`), `cloudflare/clef-omni`, `respan/span-01`, `respan/span-01-lite`, `inception/mercury-decide:free`. A model's own limit, such as too many options, fails with `decision_rejected` and the model's message.
304
+
305
+ The Input column is what each vendor lists. Images are sent to every model, and a model listed as text-only may still read them or may ignore them, so pick a model that lists images when an answer depends on one.
306
+
271
307
  ## Related APIs
272
308
 
273
309
  - [LuaDecision](./LuaDecision.md) declares a named, versioned decision that `Decisions.run` runs.
@@ -24,7 +24,7 @@ type LuaDecisionConfig = {
24
24
  name: string;
25
25
  description?: string;
26
26
  questions: Record<string, OddsQuestion | ChoiceQuestion | ScoreQuestion>;
27
- provider?: 'auto' | 'jev' | 'openai';
27
+ provider?: 'auto' | 'jev' | 'openai' | 'openrouter';
28
28
  model?: string;
29
29
  fallback?: 'jev' | 'openai' | 'none';
30
30
  region?: 'eu';
@@ -89,7 +89,7 @@ The compiler reads each decision from its source, so write it as `new LuaDecisio
89
89
 
90
90
  `lua push` stages a new version of every decision listed in `decisions`; it does not change what `Decisions.run` asks. `lua push decision --name triage` stages one decision. A staged version becomes current only through `lua version promote` or `lua decisions publish`. Until a decision has a current version, `Decisions.run` throws `decision_not_found` for it.
91
91
 
92
- A staged version goes live with the agent version that includes it: run `lua version create`, then `lua version promote <version>`. Promoting an older agent version brings back the decision versions it included, which is how you roll back a pushed decision.
92
+ A staged version goes live with the agent version that includes it: run `lua version create`, then `lua version promote <version>`. Promoting an older agent version brings back the decision versions it included, which is how you roll back a pushed decision. A decision that the promoted agent version does not include stops answering (`decision_not_found`) and keeps its versions, and promoting an agent version that includes it makes it current again.
93
93
 
94
94
  A version saved from the dashboard goes live when you publish it, and publishing an older version there rolls back too.
95
95
 
@@ -99,11 +99,11 @@ Each lua-core pod caches the current version. A promote, a publish, a rename or
99
99
 
100
100
  `lua decisions` lists the named decisions on the agent with their current version. `lua decisions versions triage` shows each version newest first, with its questions, provider and `minConfidence`.
101
101
 
102
- `lua decisions run triage --message 'I want a refund'` runs the current version and prints one line per answer, then the provider, rule, model, duration, tokens and fallback. Pass `--state '<json>'` to decide on structured state instead, and `--provider` or `--min-confidence` to override the version's options for that run.
102
+ `lua decisions run triage --message 'I want a refund'` runs the current version and prints one line per answer, then the provider, rule, model, duration, tokens and fallback. Pass `--state '<json>'` to decide on structured state instead, and `--provider`, `--model` or `--min-confidence` to override the version's options for that run. `--model liquid/d1` runs it on that model; see the model list in [Decisions](Decisions.md#other-decision-models-through-openrouter).
103
103
 
104
104
  The first push of a decision writes 1.0.1, as for every versioned primitive: a push bumps the version in `lua.skill.yaml` (1.0.0 for a new entry) before it writes.
105
105
 
106
- `lua decisions publish triage 1.0.1` makes 1.0.1 the current version straight away, which is the quickest rollback. Promoting a different agent version sets it back to what that version pins; promoting the agent version that is already active changes nothing.
106
+ `lua decisions publish triage 1.0.1` makes 1.0.1 the current version straight away, which is the quickest rollback. The next `lua version create` keeps 1.0.1 unless you push a newer version of the decision after publishing. Promoting a different agent version sets it back to what that version pins; promoting the agent version that is already active changes nothing.
107
107
 
108
108
  `lua decisions delete triage` deactivates the decision after asking and keeps its versions. From then on `Decisions.run('triage', ...)` throws `decision_not_found`. `lua decisions versions triage` still lists its versions, and publishing one of them turns it back on.
109
109
 
@@ -48,7 +48,23 @@ A typed code step. `inputSchema` / `outputSchema` / `resumeSchema` are zod schem
48
48
 
49
49
  `createWorkflow(cfg)` opens a fluent builder; `.commit()` freezes it into the compiled graph. `defineWorkflow(cfg, (wf) => …)` is sugar for the same thing.
50
50
 
51
- Config: `name`, `description?`, `inputSchema`, `outputSchema?`, `budget?` (`maxCredits`, `maxDurationSeconds`, `maxJobSeconds?`), `schedule?` (`{ type:'cron', expression, timezone }`), `scheduleInput?`, `concurrencyPolicy?` (`'allow'|'forbid'`), `concurrencyKey?` (`'correlationKey'`, requires `concurrencyPolicy:'forbid'`), `workspace?`, `outputVisibility?` (see [Output visibility](../workflows/output-visibility.md)), `env.template()` overlays (see [Env overlays](../workflows/env-overlays.md)).
51
+ Config: `name`, `description?`, `inputSchema`, `outputSchema?`, `budget?` (`maxCredits`, `maxDurationSeconds`, `maxJobSeconds?`), `schedule?` (`{ type:'cron', expression, timezone }`), `scheduleInput?`, `concurrencyPolicy?` (`'allow'|'forbid'`), `concurrencyKey?` (`'correlationKey'`, requires `concurrencyPolicy:'forbid'`, see [Concurrency](#concurrency)), `workspace?`, `outputVisibility?` (see [Output visibility](../workflows/output-visibility.md)), `env.template()` overlays (see [Env overlays](../workflows/env-overlays.md)).
52
+
53
+ ### Concurrency
54
+
55
+ `concurrencyPolicy: 'forbid'` alone is workflow-wide: one in-flight run blocks every other start of that workflow. `concurrencyKey: 'correlationKey'` narrows the guard to runs that share a `correlationKey`:
56
+
57
+ - A start is refused only while a **non-terminal** run of the same workflow carries the **same** `correlationKey`. Starts with different keys run in parallel.
58
+ - Starts with **no** `correlationKey` share one null bucket: a second keyless start is refused while a keyless run is in flight. A keyless start and a keyed start never block each other.
59
+ - `concurrencyKey` requires `concurrencyPolicy: 'forbid'`; otherwise the push is a 400 `VALIDATION_FAILED` (`concurrency-key-requires-forbid`).
60
+
61
+ The refusal is a 409 `RUNS_IN_FLIGHT` whose body carries `blockingRunId`, `blockingStatus`, `concurrencyKey` and the colliding `correlationKey` (absent for the null bucket). `lua workflows start` prints it:
62
+
63
+ ```
64
+ ❌ "outreach" has concurrencyPolicy:'forbid' and run `wfr_0` is in flight for correlationKey "release-agent-42" — wait for it or cancel it: lua workflows cancel wfr_0
65
+ ```
66
+
67
+ For the null bucket it says the in-flight run was started without a `correlationKey` and that `--correlation-key <key>` starts in parallel. A 409 with no key fields (workflow-wide `forbid`) prints the message without the key part. `--json` always carries the full error body. See [Workflows](./Workflows.md#concurrency-per-correlation-key).
52
68
 
53
69
  ### Builder verbs and the placement rule
54
70
 
@@ -14,7 +14,7 @@ Inside a sandbox (tools, jobs, webhooks, workflow code steps) `Workflows` is als
14
14
 
15
15
  - `start` **never awaits execution** — it returns `{ runId, status }` immediately. `waitSeconds` (≤ 55) asks the server to hold the response for early terminal state; it changes the response, never the execution model.
16
16
  - Runs are idempotent on `idempotencyKey` — re-sending the same key returns the existing run instead of a duplicate.
17
- - A workflow with `concurrencyPolicy: 'forbid'` refuses a second start while one is in flight with a thrown `WorkflowApiError { code: 'RUNS_IN_FLIGHT', blockingRunId }` — never a 429, never a gate.
17
+ - A workflow with `concurrencyPolicy: 'forbid'` refuses a second start while one is in flight with a thrown `WorkflowApiError { code: 'RUNS_IN_FLIGHT', blockingRunId }` — never a 429, never a gate. A per-key refusal also carries `concurrencyKey` and the colliding `correlationKey`, and `lua workflows start` prints that key (see [Concurrency](./LuaWorkflow.md#concurrency)).
18
18
  - `status: 'gated'` means the run is parked (quota, billing or consent) and holds no slot. A **consent** gate is cleared by a person resolving its approval — from the desktop, or through the approvals API / inbox. `lua workflows approve` cannot clear one: it requires an `--approval wfa_…` id, which belongs to a workflow `approval` node, and a consent gate has no such id. `lua workflows status <runId>` reports a gated run, and exits 6 under `--strict`.
19
19
  - `nameOrId` resolves the workflow **name on the calling agent first**, then a workflow id.
20
20
  - `get` / `list` honour output visibility: a restricted run comes back with `restricted: true` and no `output` — never an error.
@@ -30,6 +30,7 @@ Inside a sandbox (tools, jobs, webhooks, workflow code steps) `Workflows` is als
30
30
  - Without `concurrencyKey` nothing changes: the guard stays workflow-wide.
31
31
  - `concurrencyKey` requires `concurrencyPolicy: 'forbid'`; otherwise `lua compile` / `lua push` fail with `concurrency-key-requires-forbid`, and a push that reaches the server is a 400 `VALIDATION_FAILED` (`concurrency-key-requires-forbid`). Any value other than `'correlationKey'` also fails the compile.
32
32
  - The refusal is a 409 `RUNS_IN_FLIGHT` whose body carries `blockingRunId`, `blockingStatus`, `concurrencyKey` and the colliding `correlationKey` (absent for the null bucket).
33
+ - `lua workflows start` names the colliding `correlationKey` in its error line (and says so when the in-flight run is the keyless one); the body is unchanged under `--json`.
33
34
  - A scheduled fire that collides on its rendered `correlationKeyTemplate` key records `skipped_overlap`; a fire with a different key dispatches.
34
35
 
35
36
  ## Methods
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lua-cli",
3
- "version": "3.49.0",
3
+ "version": "3.49.2",
4
4
  "description": "Build, test, and deploy AI agents with custom tools, webhooks, and scheduled jobs. Features LuaAgent unified configuration, streaming chat, and batch deployment.",
5
5
  "readmeFilename": "README.md",
6
6
  "main": "dist/api-exports.js",
@@ -115,9 +115,9 @@
115
115
  "ts-node": "^10.9.2",
116
116
  "tsup": "^8.5.1",
117
117
  "@lua/sandbox-runtime": "0.0.1",
118
+ "@lua/shared-sandbox": "0.0.1",
118
119
  "@lua/shared-source-sync": "0.0.1",
119
120
  "@lua/shared-types": "0.0.1",
120
- "@lua/shared-sandbox": "0.0.1",
121
121
  "@lua/workflow-graph": "0.0.1"
122
122
  },
123
123
  "scripts": {
@@ -20,7 +20,7 @@
20
20
  "inquirer": "^12.9.6",
21
21
  "stripe": "^17.5.0",
22
22
  "js-yaml": "^4.1.0",
23
- "lua-cli": "^3.49.0",
23
+ "lua-cli": "^3.49.2",
24
24
  "openai": "^5.23.0",
25
25
  "uuid": "^13.0.0",
26
26
  "zod": "^3.24.1"