nexrall-code 0.5.102 → 0.5.104

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 (2) hide show
  1. package/dist/index.js +177 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9973,6 +9973,11 @@ var require_client = __commonJS({
9973
9973
  var MAX_RETRIES = 5;
9974
9974
  var RETRY_BASE_MS = 1e3;
9975
9975
  var RETRY_MAX_MS = 3e4;
9976
+ var HOUSEKEEPING_FRAMES = /* @__PURE__ */ new Set([
9977
+ "resumable",
9978
+ "context_warning",
9979
+ "tool_progress"
9980
+ ]);
9976
9981
  var MAX_TOTAL_RETRY_MS = (() => {
9977
9982
  const raw = Number(process.env.NEXRALL_MAX_RETRY_MS);
9978
9983
  return Number.isFinite(raw) && raw > 0 ? raw : 5 * 6e4;
@@ -10063,7 +10068,12 @@ var require_client = __commonJS({
10063
10068
  }
10064
10069
  ];
10065
10070
  };
10066
- const isRetryableStreamMsg = (m2) => /overloaded|rate.?limit|temporarily|unavailable|try again|internal server error/i.test(String(m2 ?? ""));
10071
+ const isRetryableStreamMsg = (m2) => {
10072
+ const s2 = String(m2 ?? "");
10073
+ if (/403[\s\S]*"type"\s*:\s*"forbidden"/i.test(s2))
10074
+ return true;
10075
+ return /overloaded|rate.?limit|temporarily|unavailable|try again|internal server error/i.test(s2);
10076
+ };
10067
10077
  let didRetry = false;
10068
10078
  let totalAttemptsMade = 0;
10069
10079
  let retryDeadline = 0;
@@ -10335,7 +10345,8 @@ var require_client = __commonJS({
10335
10345
  }
10336
10346
  const evt = parsed;
10337
10347
  lastProgressAt = Date.now();
10338
- sawModelEvent = true;
10348
+ if (!HOUSEKEEPING_FRAMES.has(evt.type))
10349
+ sawModelEvent = true;
10339
10350
  clearRetryIfNeeded();
10340
10351
  switch (evt.type) {
10341
10352
  case "text": {
@@ -10461,6 +10472,7 @@ var require_client = __commonJS({
10461
10472
  case "error": {
10462
10473
  const message = typeof evt.message === "string" ? evt.message : typeof evt.error === "string" ? evt.error : "Unknown SSE error";
10463
10474
  const notResumable = evt.notResumable === true;
10475
+ const serverSaysRetryable = evt.retryable === true;
10464
10476
  if (haveCompleteMessage()) {
10465
10477
  clearInterval(heartbeatWatchdog);
10466
10478
  onEvent({ type: "error", message });
@@ -10469,7 +10481,7 @@ var require_client = __commonJS({
10469
10481
  clearInterval(heartbeatWatchdog);
10470
10482
  stream.destroy?.();
10471
10483
  reject(Object.assign(tagTransient(new Error(message)), { forceRestart: true }));
10472
- } else if (isRetryableStreamMsg(message) && (!emittedToCaller || allowRestartAfterRender)) {
10484
+ } else if ((serverSaysRetryable || isRetryableStreamMsg(message)) && (!emittedToCaller || allowRestartAfterRender)) {
10473
10485
  clearInterval(heartbeatWatchdog);
10474
10486
  stream.destroy?.();
10475
10487
  reject(tagTransient(new Error(message)));
@@ -10494,7 +10506,8 @@ var require_client = __commonJS({
10494
10506
  });
10495
10507
  stream.on("end", () => {
10496
10508
  clearInterval(heartbeatWatchdog);
10497
- if (!sawModelEvent && !completedMessage) {
10509
+ const producedNothing = !completedMessage && textParts.length === 0 && toolUseBlocks.length === 0;
10510
+ if (producedNothing) {
10498
10511
  reject(Object.assign(new Error("Connection closed before the model responded. Retrying\u2026"), { retryable: true }));
10499
10512
  return;
10500
10513
  }
@@ -103250,6 +103263,94 @@ var require_audit = __commonJS({
103250
103263
  }
103251
103264
  });
103252
103265
 
103266
+ // ../core/dist/agent/modelCatalogue.js
103267
+ var require_modelCatalogue = __commonJS({
103268
+ "../core/dist/agent/modelCatalogue.js"(exports2) {
103269
+ "use strict";
103270
+ Object.defineProperty(exports2, "__esModule", { value: true });
103271
+ exports2.getModelCatalogue = getModelCatalogue2;
103272
+ exports2.getCachedModelCatalogue = getCachedModelCatalogue;
103273
+ exports2.liveContextWindowFor = liveContextWindowFor;
103274
+ exports2.liveMaxOutputTokensFor = liveMaxOutputTokensFor;
103275
+ exports2.liveSupportsImageInput = liveSupportsImageInput2;
103276
+ exports2.liveSupportsPdfInput = liveSupportsPdfInput2;
103277
+ exports2.liveReasoningEffortStyle = liveReasoningEffortStyle2;
103278
+ exports2.liveVendorFor = liveVendorFor2;
103279
+ exports2.liveLabelFor = liveLabelFor2;
103280
+ exports2.liveCostMultiplierFor = liveCostMultiplierFor2;
103281
+ exports2.liveSelectableModelIds = liveSelectableModelIds2;
103282
+ exports2._resetModelCatalogueCacheForTests = _resetModelCatalogueCacheForTests;
103283
+ var client_1 = require_client();
103284
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
103285
+ var _state2 = null;
103286
+ var _inFlight = null;
103287
+ async function getModelCatalogue2(opts = {}) {
103288
+ const fresh = _state2 && Date.now() - _state2.fetchedAt < CACHE_TTL_MS;
103289
+ if (fresh && !opts.force)
103290
+ return _state2;
103291
+ if (_inFlight)
103292
+ return _inFlight;
103293
+ _inFlight = (async () => {
103294
+ try {
103295
+ const { models, defaultModel } = await (0, client_1.getCodeModels)();
103296
+ if (!models.length)
103297
+ return _state2;
103298
+ const next = { models, defaultModel, fetchedAt: Date.now() };
103299
+ _state2 = next;
103300
+ return next;
103301
+ } catch {
103302
+ return _state2;
103303
+ } finally {
103304
+ _inFlight = null;
103305
+ }
103306
+ })();
103307
+ return _inFlight;
103308
+ }
103309
+ function getCachedModelCatalogue() {
103310
+ return _state2;
103311
+ }
103312
+ function findModel(id) {
103313
+ if (!id || !_state2)
103314
+ return void 0;
103315
+ return _state2.models.find((m2) => m2.id === id);
103316
+ }
103317
+ function liveContextWindowFor(id, fallback) {
103318
+ return findModel(id)?.contextWindow ?? fallback;
103319
+ }
103320
+ function liveMaxOutputTokensFor(id, fallback) {
103321
+ return findModel(id)?.maxOutputTokens ?? fallback;
103322
+ }
103323
+ function liveSupportsImageInput2(id, fallback) {
103324
+ const m2 = findModel(id);
103325
+ return m2 ? m2.supportsImageInput : fallback;
103326
+ }
103327
+ function liveSupportsPdfInput2(id, fallback) {
103328
+ const m2 = findModel(id);
103329
+ return m2 ? m2.supportsPdfInput : fallback;
103330
+ }
103331
+ function liveReasoningEffortStyle2(id) {
103332
+ return findModel(id)?.reasoningEffortStyle;
103333
+ }
103334
+ function liveVendorFor2(id) {
103335
+ return findModel(id)?.vendor;
103336
+ }
103337
+ function liveLabelFor2(id, fallback) {
103338
+ return findModel(id)?.label ?? fallback;
103339
+ }
103340
+ function liveCostMultiplierFor2(id, fallback) {
103341
+ const m2 = findModel(id);
103342
+ return m2 && typeof m2.costMultiplier === "number" ? m2.costMultiplier : fallback;
103343
+ }
103344
+ function liveSelectableModelIds2() {
103345
+ return _state2 ? _state2.models.filter((m2) => m2.available !== false).map((m2) => m2.id) : null;
103346
+ }
103347
+ function _resetModelCatalogueCacheForTests() {
103348
+ _state2 = null;
103349
+ _inFlight = null;
103350
+ }
103351
+ }
103352
+ });
103353
+
103253
103354
  // ../core/dist/agent/loop.js
103254
103355
  var require_loop = __commonJS({
103255
103356
  "../core/dist/agent/loop.js"(exports2) {
@@ -103355,6 +103456,7 @@ var require_loop = __commonJS({
103355
103456
  var audit_1 = require_audit();
103356
103457
  var memory_1 = require_memory();
103357
103458
  var safeSlice_1 = require_safeSlice();
103459
+ var modelCatalogue_1 = require_modelCatalogue();
103358
103460
  var fs9 = __importStar(__require("fs"));
103359
103461
  var path8 = __importStar(__require("path"));
103360
103462
  var child_process_1 = __require("child_process");
@@ -104184,6 +104286,16 @@ ${partial}` : "",
104184
104286
  "gpt-5.4-mini": 272e3,
104185
104287
  "gpt-4.1": 1047576,
104186
104288
  "gpt-4o-mini": 128e3,
104289
+ // GPT-5.6 family: MEASURED 2026-09-03 via a 400 (same technique as
104290
+ // gpt-5.4's 922_000 above) — and it is the EXACT SAME 922,000-token
104291
+ // ceiling on all three sizes. This CONTRADICTS the publicly documented
104292
+ // figure (openai.com/index/gpt-5-6 + OpenRouter's model card both
104293
+ // advertise 1,050,000) — see backend services/providers/modelRegistry.js's
104294
+ // own comment on these rows for the measured 400 body. Guessing 1.05M here
104295
+ // would fire auto-compaction ~12% past the real wall.
104296
+ "gpt-5.6-sol": 922e3,
104297
+ "gpt-5.6-terra": 922e3,
104298
+ "gpt-5.6-luna": 922e3,
104187
104299
  // DeepSeek, by real model id (documented — see backend/services/providers/
104188
104300
  // modelRegistry.js's own TODO(unverified-by-400): DeepSeek accepts an
104189
104301
  // oversized max_completion_tokens without rejecting it, so there was no 400
@@ -104202,7 +104314,8 @@ ${partial}` : "",
104202
104314
  "glm-5.3": 1048576
104203
104315
  };
104204
104316
  function contextWindowFor2(model) {
104205
- return MODEL_CONTEXT_TOKENS[model ?? "turbo"] ?? 128e3;
104317
+ const fallback = MODEL_CONTEXT_TOKENS[model ?? "turbo"] ?? 128e3;
104318
+ return (0, modelCatalogue_1.liveContextWindowFor)(model, fallback);
104206
104319
  }
104207
104320
  function envFraction(name, fallback) {
104208
104321
  const v = Number(process.env[name]);
@@ -104523,7 +104636,7 @@ Continue the work from here.` }] });
104523
104636
  const settings = (0, rules_1.loadSettings)(opts.workDir);
104524
104637
  if (!resolveAutoCompact(void 0, settings.raw))
104525
104638
  return false;
104526
- const contextWindow = MODEL_CONTEXT_TOKENS[opts.model ?? "turbo"] ?? 1e6;
104639
+ const contextWindow = (0, modelCatalogue_1.liveContextWindowFor)(opts.model, MODEL_CONTEXT_TOKENS[opts.model ?? "turbo"] ?? 1e6);
104527
104640
  let bodyBytes = estimateBodyBytes2(messages);
104528
104641
  let tokenGuess = estimateTokensRough2(messages);
104529
104642
  const overPruneThreshold = () => tokenGuess > contextWindow * AUTO_PRUNE_THRESHOLD || bodyBytes > MAX_BODY_BYTES;
@@ -104638,7 +104751,7 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
104638
104751
  const autoContinue = resolveAutoContinue(options.autoContinue, settings.raw);
104639
104752
  const autoCompact = resolveAutoCompact(options.autoCompact, settings.raw);
104640
104753
  const verifyNudgeOn = resolveVerificationNudge(settings.raw);
104641
- const contextWindow = MODEL_CONTEXT_TOKENS[model] ?? 2e5;
104754
+ const contextWindow = (0, modelCatalogue_1.liveContextWindowFor)(model, MODEL_CONTEXT_TOKENS[model] ?? 2e5);
104642
104755
  let lastPromptTokens = 0;
104643
104756
  let compacting = false;
104644
104757
  const hardCap = autoContinue ? Math.max(maxIterations, MAX_ITERATIONS_CEILING) : maxIterations;
@@ -107326,6 +107439,7 @@ var require_dist3 = __commonJS({
107326
107439
  __exportStar(require_client(), exports2);
107327
107440
  __exportStar(require_executor(), exports2);
107328
107441
  __exportStar(require_loop(), exports2);
107442
+ __exportStar(require_modelCatalogue(), exports2);
107329
107443
  __exportStar(require_testIntegrity(), exports2);
107330
107444
  __exportStar(require_editCompleteness(), exports2);
107331
107445
  __exportStar(require_securityLint(), exports2);
@@ -153152,6 +153266,9 @@ var MODEL_LABELS = {
153152
153266
  "claude-opus-5": "Claude Opus 5",
153153
153267
  "claude-fable-5": "Claude Fable 5",
153154
153268
  "claude-haiku-4-5-20251001": "Claude Haiku 4.5",
153269
+ "gpt-5.6-sol": "GPT-5.6 Sol",
153270
+ "gpt-5.6-terra": "GPT-5.6 Terra",
153271
+ "gpt-5.6-luna": "GPT-5.6 Luna",
153155
153272
  "gpt-5.4": "GPT-5.4",
153156
153273
  "gpt-5.4-mini": "GPT-5.4 Mini",
153157
153274
  "gpt-4.1": "GPT-4.1",
@@ -153172,6 +153289,9 @@ var SELECTABLE_MODELS = [
153172
153289
  "claude-sonnet-5",
153173
153290
  "claude-opus-5",
153174
153291
  "claude-fable-5",
153292
+ "gpt-5.6-sol",
153293
+ "gpt-5.6-terra",
153294
+ "gpt-5.6-luna",
153175
153295
  "gpt-5.4",
153176
153296
  "gpt-5.4-mini",
153177
153297
  "gpt-4.1",
@@ -153184,6 +153304,13 @@ var MODEL_COST_MULTIPLIER = {
153184
153304
  "claude-sonnet-5": 1.5,
153185
153305
  "claude-opus-5": 3,
153186
153306
  "claude-fable-5": 5,
153307
+ // GPT-5.6 family (2026-07 launch, Terra/Luna repriced 2026-07-30) — same
153308
+ // relative-to-gpt-4.1/qwen3.7-max (=1x) signal as every other row here,
153309
+ // off backend migration 132's per-1M-token seed prices (Sol $5/$30, Terra
153310
+ // $2/$12, Luna $0.20/$1.20 vs gpt-4.1's $2/$8).
153311
+ "gpt-5.6-sol": 2,
153312
+ "gpt-5.6-terra": 1.2,
153313
+ "gpt-5.6-luna": 0.1,
153187
153314
  "gpt-5.4": 2,
153188
153315
  "gpt-5.4-mini": 0.5,
153189
153316
  "gpt-4.1": 1,
@@ -153197,17 +153324,29 @@ var MODEL_COST_MULTIPLIER = {
153197
153324
  "glm-5.3": 0.6
153198
153325
  };
153199
153326
  function modelWithCostHint(id) {
153200
- const mult = MODEL_COST_MULTIPLIER[id];
153327
+ const mult = (0, import_code_core4.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
153201
153328
  return mult === void 0 ? id : `${id} (${mult}x)`;
153202
153329
  }
153203
153330
  var NO_VISION_MODELS = /* @__PURE__ */ new Set(["deepseek-v4-pro", "deepseek-v4-flash", "qwen3.7-max", "glm-5.3"]);
153204
153331
  var NO_PDF_MODELS = /* @__PURE__ */ new Set([
153332
+ "gpt-5.6-sol",
153333
+ "gpt-5.6-terra",
153334
+ "gpt-5.6-luna",
153205
153335
  "gpt-5.4",
153206
153336
  "gpt-5.4-mini",
153207
153337
  "gpt-4.1",
153208
153338
  "gpt-4o-mini",
153209
153339
  ...NO_VISION_MODELS
153210
153340
  ]);
153341
+ function needsVisionSidecar(model) {
153342
+ return !(0, import_code_core4.liveSupportsImageInput)(model, !NO_VISION_MODELS.has(model));
153343
+ }
153344
+ function needsPdfSidecar(model) {
153345
+ return !(0, import_code_core4.liveSupportsPdfInput)(model, !NO_PDF_MODELS.has(model));
153346
+ }
153347
+ function selectableModelIds() {
153348
+ return (0, import_code_core4.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
153349
+ }
153211
153350
  function normaliseModelId(model) {
153212
153351
  const raw = (model ?? "").trim();
153213
153352
  if (!raw)
@@ -153215,6 +153354,14 @@ function normaliseModelId(model) {
153215
153354
  return LEGACY_MODEL_ALIASES[raw.toLowerCase()] ?? raw;
153216
153355
  }
153217
153356
  var MODEL_EFFORT_STYLE = {
153357
+ // GPT-5.6 Sol/Terra/Luna hit the SAME "reasoning_effort + tools" 400 as
153358
+ // gpt-5.4/mini below (confirmed by multiple third-party integrations
153359
+ // 2026-07: github.com/BerriAI/litellm#33221, github.com/danny-avila/
153360
+ // LibreChat#14231) — OPENAI_GATED already handles that (skip when tools
153361
+ // are present), so no new style is needed for this family.
153362
+ "gpt-5.6-sol": "openai_gated",
153363
+ "gpt-5.6-terra": "openai_gated",
153364
+ "gpt-5.6-luna": "openai_gated",
153218
153365
  "gpt-5.4": "openai_gated",
153219
153366
  "gpt-5.4-mini": "openai_gated",
153220
153367
  "gpt-4.1": "none",
@@ -153251,8 +153398,21 @@ var EFFORT_STYLE_CONFIG = {
153251
153398
  // No reasoning-effort concept on this model at all (gpt-4.1, gpt-4o-mini).
153252
153399
  none: { levels: ["medium"], names: ["N/A"] }
153253
153400
  };
153401
+ var _BACKEND_STYLE_TO_LOCAL = {
153402
+ openai_gated: "openai_gated",
153403
+ deepseek: "deepseek",
153404
+ qwen_thinking_toggle: "qwen",
153405
+ glm_thinking_level: "glm",
153406
+ none: "none"
153407
+ };
153254
153408
  function effortStyleFor(modelId) {
153255
- return MODEL_EFFORT_STYLE[normaliseModelId(modelId)] ?? "anthropic";
153409
+ const id = normaliseModelId(modelId);
153410
+ if ((0, import_code_core4.liveVendorFor)(id) === "Anthropic")
153411
+ return "anthropic";
153412
+ const liveStyle = (0, import_code_core4.liveReasoningEffortStyle)(id);
153413
+ if (liveStyle && _BACKEND_STYLE_TO_LOCAL[liveStyle])
153414
+ return _BACKEND_STYLE_TO_LOCAL[liveStyle];
153415
+ return MODEL_EFFORT_STYLE[id] ?? "anthropic";
153256
153416
  }
153257
153417
  function effortConfigFor(modelId) {
153258
153418
  return EFFORT_STYLE_CONFIG[effortStyleFor(modelId)];
@@ -153269,7 +153429,7 @@ function defaultEffortForModel(modelId) {
153269
153429
  }
153270
153430
  function resolveModelLabel(alias) {
153271
153431
  const id = normaliseModelId(alias);
153272
- return MODEL_LABELS[id] ?? id;
153432
+ return (0, import_code_core4.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
153273
153433
  }
153274
153434
  function tryExec(cmd, cwd2) {
153275
153435
  try {
@@ -153897,6 +154057,8 @@ async function startChatSession(options) {
153897
154057
  const headless = options.outputFormat === "json" || options.outputFormat === "stream-json";
153898
154058
  const { workDir } = options;
153899
154059
  let modelAlias = options.model;
154060
+ void (0, import_code_core4.getModelCatalogue)().catch(() => {
154061
+ });
153900
154062
  const canPrompt = !headless && !options.prompt && !options.stdinText && process.stdin.isTTY === true && process.stdout.isTTY === true;
153901
154063
  if (!trustGrantedByEnv()) {
153902
154064
  if (!canPrompt) {
@@ -154438,10 +154600,11 @@ ${dirList}`;
154438
154600
  return;
154439
154601
  case "/model": {
154440
154602
  const requested = arg.trim();
154603
+ const models = selectableModelIds();
154441
154604
  if (!requested) {
154442
154605
  console.log(source_default.dim(` Current: ${source_default.cyan(resolveModelLabel(modelAlias))}`));
154443
- console.log(source_default.dim(` Options: ${SELECTABLE_MODELS.map(modelWithCostHint).join(" \xB7 ")}`));
154444
- } else if (SELECTABLE_MODELS.includes(normaliseModelId(requested))) {
154606
+ console.log(source_default.dim(` Options: ${models.map(modelWithCostHint).join(" \xB7 ")}`));
154607
+ } else if (models.includes(normaliseModelId(requested))) {
154445
154608
  modelAlias = normaliseModelId(requested);
154446
154609
  const clamped = clampEffortForModel(effortLevel, modelAlias);
154447
154610
  const effortChanged = clamped !== effortLevel;
@@ -154451,7 +154614,7 @@ ${dirList}`;
154451
154614
  console.log(source_default.dim(` Effort reset \u2192 ${effortLevel} (previous level not available on this model)`));
154452
154615
  }
154453
154616
  } else {
154454
- console.log(source_default.red(` Unknown: ${requested}. Options: ${SELECTABLE_MODELS.join(", ")}`));
154617
+ console.log(source_default.red(` Unknown: ${requested}. Options: ${models.join(", ")}`));
154455
154618
  }
154456
154619
  rl.prompt();
154457
154620
  return;
@@ -154580,7 +154743,7 @@ ${content}
154580
154743
  const label = caption ? `[Attached: ${rel}]
154581
154744
  ${caption}` : `[Attached: ${rel}]`;
154582
154745
  const content = [{ type: "text", text: label }];
154583
- const needsSidecar = isPdf ? NO_PDF_MODELS.has(model) : NO_VISION_MODELS.has(model);
154746
+ const needsSidecar = isPdf ? needsPdfSidecar(model) : needsVisionSidecar(model);
154584
154747
  if (needsSidecar) {
154585
154748
  console.log(source_default.dim(` ${resolveModelLabel(modelAlias)} can't read ${isPdf ? "PDFs" : "images"} directly \u2014 describing ${rel} with Claude Sonnet 5 first\u2026`));
154586
154749
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.102",
3
+ "version": "0.5.104",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",