@lawaty/lacode 2.3.0-beta.5 → 2.3.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,31 @@ commit as the changelog entry it describes.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [2.3.0-beta.6] — fallback capacity and honest failure benching
13
+
14
+ ### Added
15
+
16
+ - Pool: `pool.txt` now supports a fallback section (`# --- fallback ---`) used only when no primary slot is usable. Fallback candidates are score-ranked and must spend a different quota window from every primary slot; a seat carries weight 2 (was 1), a provider may contribute up to two models, and up to five fallback seats are kept.
17
+ - Pool: `pool.txt` supports `# exclude-provider: name[,name]` in its header. A listed provider is never probed and is removed from the primary selection, the fallback selection, and the retention source. The header survives refresh, so the exclusion persists; deleting the line re-admits the provider.
18
+ - Pool: slots accept `ceil:<max-parallel>` as a hard concurrency cap (the pool's capacity is the sum of ceilings) and `c:<context>` as a context fact. The status output shows the ceiling when it differs from the weight and tags the fallback section.
19
+ - Pool: consecutive-failure streaks are carried in `model-scores.json`, so a model that keeps failing can be retired instead of retained indefinitely.
20
+ - Provider compatibility: the transport retries the identical replayable request up to three times when `opencode.ai/inference` answers HTTP 400 "`summary` is not a supported value for `reasoning_display`". It is a flaky upstream, no client field changes the rate, and the model is never disabled or dropped.
21
+
22
+ ### Changed
23
+
24
+ - Pool: a model whose current probe is a transient failure is retired after three consecutive failing scans (`MAX_TRANSIENT_STREAK`); a successful scan resets the streak.
25
+ - Pool: the refresh's `scan-kept` bench now spans the six-hour refresh interval instead of 45 minutes. A successful probe clears only a `scan-kept` record; runtime strikes (`rate-limited`, `no answer`, `provider error`, ...) keep their escalation count.
26
+ - Pool: `API budget exhausted`, `quota exhausted`, and bare credit/billing failures are classified as credit, and credit exhaustion now vacates the slot instead of retaining a dead seat. A later answering scan re-admits the model.
27
+ - Pool: a terminal session error that is not a recognised model signal now benches the model on the ordinary strike curve instead of only being logged. `MessageAbortedError` (a user interrupt or the pool's own failover) is never counted as a model failure.
28
+ - Pool: `budget exhausted` is recognised as a quota reason, so it takes the longer quota cooldown curve.
29
+ - Pool: the scorer deliberately does not include `rate-limited` in the provider breaker. A 429 answers in milliseconds, and truncating on it leaves the provider's pool members unscanned, so their failure streak could never advance.
30
+
31
+ ### Fixed
32
+
33
+ - Pool: an upstream model served by two provider aliases (`openrouter/...` and `console-openrouter/...`) no longer occupies two primary slots when the model id past the provider prefix is identical.
34
+ - Pool: the `reasoning_display` 400 is classified as a transient upstream fault (`upstream-transient`) and cooled for one short minute instead of being misread as a capability mismatch and benched for six hours. A genuinely unsupported parameter is still benched as `invalid-request`.
35
+ - Pool: `pool_status` judges structure on the primary pool and tags the fallback section, so a fallback line no longer looks like a broken tail.
36
+
12
37
  ## [2.3.0-beta.5] — bench in place, cheap recovery, visible slots
13
38
 
14
39
  ### Added
package/README.md CHANGED
@@ -244,11 +244,19 @@ bare name is the only thing that works: a hand-named variant such as `explore-1`
244
244
  is coerced to its base and routed by the pool, so naming one cannot pin a
245
245
  provider or skip ordered slot filling.
246
246
 
247
- A slot's `weight` is both its concurrency budget and its cap: the pool fills
248
- slots in declaration order in one pass, then overflows to the least-loaded slot.
249
- There is no slot cap, and a model may appear on more than one line — each line
250
- is a separate slot with its own weight — so to give a model more capacity, list
251
- it again.
247
+ A slot's `weight` is its per-pass concurrency budget, and its default cap. A
248
+ larger optional `ceiling` (max parallel sessions; flat form `ceil:<n>`) lets a
249
+ later pass deepen the slot past its weight. The pool fills slots in declaration
250
+ order; when every usable slot has reached its weight, later passes fill toward
251
+ each slot's ceiling, then overflow goes to the least-loaded slot. There is no
252
+ slot cap, and a model may appear on more than one line — each line is a separate
253
+ slot with its own weight and ceiling — so to give a model more capacity, raise
254
+ its ceiling or list it again.
255
+
256
+ A pool file may carry a fallback section (`# --- fallback ---` in `pool.txt`, or
257
+ `fallbackSlots` in JSON). Those slots are used only when every primary slot is
258
+ unavailable; a fallback slot that shares a quota bucket with the primary is
259
+ rejected so the same provider limit cannot bench both pools.
252
260
 
253
261
  A model that rate-limits or runs out of quota is benched until its window ends,
254
262
  so one sick provider does not take the pool with it.
package/dist/agents.js CHANGED
@@ -42,6 +42,7 @@ var COOLDOWN_MAX_MS = 15 * 60 * 1e3;
42
42
  var QUOTA_COOLDOWN_BASE_MS = 15 * 60 * 1e3;
43
43
  var QUOTA_COOLDOWN_MAX_MS = 6 * 60 * 60 * 1e3;
44
44
  var STRIKE_DECAY_MS = 30 * 60 * 1e3;
45
+ var TRANSIENT_UPSTREAM_COOLDOWN_MS = 60 * 1e3;
45
46
  var PRESETS_DIR = join2(PACKAGE_ROOT, "presets");
46
47
  var DEFAULT_PRESET = join2(PRESETS_DIR, "free-tier.json");
47
48
  var USER_CONFIG_DIR = join2(homedir(), ".config", "lacode");
package/dist/commands.js CHANGED
@@ -47,6 +47,7 @@ var COOLDOWN_MAX_MS = 15 * 60 * 1e3;
47
47
  var QUOTA_COOLDOWN_BASE_MS = 15 * 60 * 1e3;
48
48
  var QUOTA_COOLDOWN_MAX_MS = 6 * 60 * 60 * 1e3;
49
49
  var STRIKE_DECAY_MS = 30 * 60 * 1e3;
50
+ var TRANSIENT_UPSTREAM_COOLDOWN_MS = 60 * 1e3;
50
51
  var PRESETS_DIR = join2(PACKAGE_ROOT, "presets");
51
52
  var DEFAULT_PRESET = join2(PRESETS_DIR, "free-tier.json");
52
53
  var USER_CONFIG_DIR = join2(homedir(), ".config", "lacode");
@@ -160,7 +161,7 @@ Follow the rules for the file in your system prompt, then report what you change
160
161
  Print one PASS/FAIL line per check below, with the exact fix for any FAIL.
161
162
 
162
163
  1. Pool status \u2014 call the \`pool_status\` tool. Report the slot list, each
163
- slot's load against its budget (its weight), and which models
164
+ slot's load against its ceiling, and which models
164
165
  are cooling.
165
166
 
166
167
  2. OmniRoute configured \u2014 inspect the \`@lawaty/lacode\` plugin options in the
@@ -196,9 +197,11 @@ The pool is ONE global slot table shared by every pooled base (explore, implemen
196
197
  1. Establish the baseline. Read \`${USER_MODELS_TXT}\`. If it is absent, the pool is resolving from the bundled preset \`presets/free-tier.json\`; read that file to learn what is in effect. Call the \`pool_status\` tool only if you need live load or cooldown state.
197
198
 
198
199
  2. Resolve the instruction above into a target slot table. These are the rules the loader enforces \u2014 never write a file that breaks one:
199
- - one slot per line: \`<provider>/<model-id> [weight]\`
200
+ - one slot per line: \`<provider>/<model-id> [weight] [ceil:<max-parallel>] [c:<context>] [b:<bucket>]\`
200
201
  - any number of slots; a model may appear on more than one line, and each line is its own slot with its own budget
201
- - weight is optional (default 1) and must be a number in (0, ${MAX_WEIGHT}]. It is the slot's active-session budget \u2014 how many concurrent sessions it may hold. The pool's total capacity is the sum of the weights. NOT a priority; pick order follows line order.
202
+ - weight is optional (default 1) and must be a number in (0, ${MAX_WEIGHT}]. It is the slot's per-pass budget \u2014 how many concurrent sessions one pass may add. The pool's total capacity is the sum of the ceilings (which equals the sum of the weights when no ceiling is set). NOT a priority; pick order follows line order.
203
+ - \`ceil:<max-parallel>\` is optional and is the slot's hard concurrency cap (its max parallel sessions). It must be at least the weight; later passes deepen a slot toward it after every slot has had its weight. Absent means the ceiling equals the weight (one pass).
204
+ - a line that is exactly \`# --- fallback ---\` starts the fallback section. Keep it and the slots below it when rewriting the file: those slots are used only when no primary slot is usable, and each must not share a bucket or model with the primary.
202
205
  - \`#\` starts a comment (whole-line or trailing); blank lines are ignored.
203
206
  If the instruction names a model ambiguously, stop and ask rather than guessing.
204
207