axstack 0.38.0 → 0.39.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.
@@ -235,15 +235,24 @@ This opt-in script changes only root `defaultModelSelection` in
235
235
  `~/.t3/userdata/settings.json`. Without a pool it changes only the instance
236
236
  among enabled accounts of the current Claude/Codex driver.
237
237
  `AXSTACK_SYNC_POOL` compares drivers by weekly clocks (>=604800s), without
238
- plan weights. Rotation needs a ten-point elapsed-minus-used pace lead;
239
- excluded/unauthorized/skipped current accounts can escape to known candidates.
240
- Any window at 95% or a reached limit excludes. Non-current absent credentials
238
+ plan weights. Weekly remaining quota is `R = 100 - used` for the selected
239
+ weekly window. Prefer the highest band: `R >= 15`, `10 <= R < 15`, then `R < 10`.
240
+ A higher band switches without a pace margin; within the same band, rotation
241
+ needs a ten-point elapsed-minus-used pace lead and the current account wins ties.
242
+ This also applies when every eligible account is below 10% remaining: known
243
+ usable quota stays usable, ranked by pace. Without a pool, only pace applies.
244
+ This is a best-effort weekly reserve preference, not a guaranteed budget for
245
+ future work. The current hard guard still excludes any window at 95% used
246
+ or a reached limit. Excluded/unauthorized/skipped current accounts can escape
247
+ to known candidates before the unknown hold; otherwise any unknown holds,
248
+ even below 10% remaining. No eligible candidate leaves the default untouched.
249
+ Non-current absent credentials
241
250
  exclude; malformed/unreadable credentials and unknown/stale usage hold.
242
251
  Cache expires after five minutes. Missing defaults stay untouched; OAuth tokens
243
252
  are never refreshed. Each host's pool uses only enabled, credentialed drivers:
244
253
 
245
254
  ```sh
246
- AXSTACK_SYNC_POOL=claudeAgent=claude-opus-5-5,codex=gpt-6.1-sol:xhigh \
255
+ AXSTACK_SYNC_POOL=claudeAgent=claude-opus-5-5,codex=gpt-6-astra:medium \
247
256
  bun ~/.agents/skills/axstack/scripts/sync-default.js --dry-run
248
257
  ```
249
258
 
@@ -258,6 +267,15 @@ A fsynced sibling preserves mode; original bytes are checked before rename and
258
267
  read back afterward. The accepted sub-ms T3 write race can lose an edit;
259
268
  read-back mismatch is an error.
260
269
 
270
+ Validate pool models and efforts against each host's catalog before activation.
271
+ Preserve that host's current Opus effort by including it explicitly when set;
272
+ the illustrative Claude entry above omits effort. If the current Codex default
273
+ still uses Sol, the driver performs a one-time same-account selection migration
274
+ to `gpt-6-astra:medium` during authorized host activation, saving rollback bytes
275
+ and verifying the write/readback. The helper does not enforce models when the
276
+ account stays selected. Only new chats use the changed default; running threads,
277
+ role routing, schedules and `roles.json` stay outside this policy.
278
+
261
279
  Under host-mutation authority, install and enable:
262
280
 
263
281
  ```sh
@@ -277,7 +295,7 @@ Override Bun/skills paths via `ExecStart`/`Environment` in
277
295
 
278
296
  ```ini
279
297
  [Service]
280
- Environment=AXSTACK_SYNC_POOL=claudeAgent=claude-opus-5-5,codex=gpt-6.1-sol:xhigh
298
+ Environment=AXSTACK_SYNC_POOL=claudeAgent=claude-opus-5-5,codex=gpt-6-astra:medium
281
299
  ```
282
300
 
283
301
  Reload after edits. Logout needs authorized linger; installation never activates units.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axstack",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Axstack installer and setup CLI: installs owned chat skills and role data, configures supported harness settings, and checks T3 Code capabilities.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -39,17 +39,21 @@ export function decide(settings, usage, now, pool = null) {
39
39
  && Number.isFinite(window?.used) && Number.isFinite(window?.seconds) && window.seconds > 0
40
40
  && Number.isFinite(window?.resetAt) && window.resetAt > now
41
41
  && (!pooled || window.seconds >= 604800);
42
+ const remaining = known ? 100 - window.used : null;
42
43
  return { instanceId, state: excluded ? 'excluded' : known ? 'eligible' : 'unknown',
44
+ ...(pooled && { band: known ? remaining >= 15 ? 2 : remaining >= 10 ? 1 : 0 : null }),
43
45
  pace: known ? 100 * (1 - (window.resetAt - now) / (window.seconds * 1000)) - window.used : null };
44
46
  });
45
47
  const current = result.instances.find((entry) => entry.instanceId === from);
46
- const best = result.instances.filter((entry) => entry.state === 'eligible').sort((a, b) => b.pace - a.pace)[0];
48
+ const best = result.instances.filter((entry) => entry.state === 'eligible')
49
+ .sort((a, b) => (pooled ? b.band - a.band : 0) || b.pace - a.pace)[0];
47
50
  const excluded = !current || current.state === 'excluded';
48
51
  if (excluded && best) return updated(best, 'current-excluded');
49
52
  if (result.instances.some((entry) => entry.state === 'unknown')) return { ...result, reason: 'unknown' };
50
53
  if (!best) return { ...result, reason: 'no-eligible' };
51
- if (best.instanceId !== from && best.pace - current.pace >= 10) {
52
- return updated(best, 'pace');
54
+ const higherBand = pooled && best.band > current.band;
55
+ if (best.instanceId !== from && (higherBand || best.pace - current.pace >= 10)) {
56
+ return updated(best, higherBand ? 'reserve' : 'pace');
53
57
  }
54
58
  return { ...result, reason: 'margin' };
55
59
  }