nexrall-code 0.5.100 → 0.5.102
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/dist/index.js +105 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -153214,6 +153214,59 @@ function normaliseModelId(model) {
|
|
|
153214
153214
|
return "claude-sonnet-5";
|
|
153215
153215
|
return LEGACY_MODEL_ALIASES[raw.toLowerCase()] ?? raw;
|
|
153216
153216
|
}
|
|
153217
|
+
var MODEL_EFFORT_STYLE = {
|
|
153218
|
+
"gpt-5.4": "openai_gated",
|
|
153219
|
+
"gpt-5.4-mini": "openai_gated",
|
|
153220
|
+
"gpt-4.1": "none",
|
|
153221
|
+
"gpt-4o-mini": "none",
|
|
153222
|
+
"deepseek-v4-pro": "deepseek",
|
|
153223
|
+
"deepseek-v4-flash": "deepseek",
|
|
153224
|
+
"qwen3.7-max": "qwen",
|
|
153225
|
+
"glm-5.3": "glm"
|
|
153226
|
+
// Everything else (all claude-* ids) falls through to 'anthropic' below.
|
|
153227
|
+
};
|
|
153228
|
+
var EFFORT_STYLE_CONFIG = {
|
|
153229
|
+
// Anthropic's real output_config.effort enum is low|medium|high|xhigh|max
|
|
153230
|
+
// (docs.claude.com extended output). 'extra'/'ultra' are legacy UI labels
|
|
153231
|
+
// this codebase invented, translated server-side by routes/code.js's
|
|
153232
|
+
// EFFORT_MAP (extra->xhigh, ultra->max) — kept as the wire values here
|
|
153233
|
+
// (rather than switching to 'xhigh'/'max' directly) only for backward
|
|
153234
|
+
// compatibility with saved sessions/scripts already passing them.
|
|
153235
|
+
anthropic: { levels: ["low", "medium", "high", "extra", "ultra"], names: ["Low", "Medium", "High", "Extra High", "Max"] },
|
|
153236
|
+
// DeepSeek V4 has THREE real buckets — live-verified against
|
|
153237
|
+
// api.deepseek.com 2026-08-30 (see streamFactory.test.js's DEEPSEEK case):
|
|
153238
|
+
// low->low, medium/high/xhigh->high, max->max.
|
|
153239
|
+
deepseek: { levels: ["low", "high", "max"], names: ["Low", "Standard", "Max"] },
|
|
153240
|
+
// Qwen3.7-Max's knob is a boolean (enable_thinking), not a graded scale.
|
|
153241
|
+
qwen: { levels: ["low", "high"], names: ["Thinking Off", "Thinking On"] },
|
|
153242
|
+
// GLM-5.3 cannot disable reasoning at all (a hard 400 if you try) and has
|
|
153243
|
+
// 3 real buckets (low/high/max) via a FLAT top-level `reasoning_effort`
|
|
153244
|
+
// string — live-verified against api.z.ai 2026-08-30 (see
|
|
153245
|
+
// streamFactory.test.js's GLM_THINKING_LEVEL case).
|
|
153246
|
+
glm: { levels: ["low", "high", "max"], names: ["Low", "High", "Max"] },
|
|
153247
|
+
// Effectively a no-op today whenever the turn has tools (almost always true
|
|
153248
|
+
// for nexrall-code) — see backend modelRegistry.js's OPENAI_GATED doc
|
|
153249
|
+
// comment. Still forwards a real value for the rare tool-less turn.
|
|
153250
|
+
openai_gated: { levels: ["low", "medium", "high"], names: ["Low", "Medium", "High"] },
|
|
153251
|
+
// No reasoning-effort concept on this model at all (gpt-4.1, gpt-4o-mini).
|
|
153252
|
+
none: { levels: ["medium"], names: ["N/A"] }
|
|
153253
|
+
};
|
|
153254
|
+
function effortStyleFor(modelId) {
|
|
153255
|
+
return MODEL_EFFORT_STYLE[normaliseModelId(modelId)] ?? "anthropic";
|
|
153256
|
+
}
|
|
153257
|
+
function effortConfigFor(modelId) {
|
|
153258
|
+
return EFFORT_STYLE_CONFIG[effortStyleFor(modelId)];
|
|
153259
|
+
}
|
|
153260
|
+
function clampEffortForModel(effort, modelId) {
|
|
153261
|
+
const config = effortConfigFor(modelId);
|
|
153262
|
+
if (config.levels.includes(effort))
|
|
153263
|
+
return effort;
|
|
153264
|
+
return defaultEffortForModel(modelId);
|
|
153265
|
+
}
|
|
153266
|
+
function defaultEffortForModel(modelId) {
|
|
153267
|
+
const levels = effortConfigFor(modelId).levels;
|
|
153268
|
+
return levels[Math.min(1, levels.length - 1)];
|
|
153269
|
+
}
|
|
153217
153270
|
function resolveModelLabel(alias) {
|
|
153218
153271
|
const id = normaliseModelId(alias);
|
|
153219
153272
|
return MODEL_LABELS[id] ?? id;
|
|
@@ -153664,6 +153717,41 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
153664
153717
|
resultText += text;
|
|
153665
153718
|
emit({ type: "text", text });
|
|
153666
153719
|
},
|
|
153720
|
+
// ── Reasoning-phase liveness ──────────────────────────────────────
|
|
153721
|
+
// These used to be UNWIRED here while runTurn() (interactive) wired all
|
|
153722
|
+
// three, and the asymmetry was not cosmetic: on a reasoning model at high
|
|
153723
|
+
// effort the thinking phase emits no text and no tool calls, so a headless
|
|
153724
|
+
// consumer saw ZERO events for the entire phase and could not distinguish
|
|
153725
|
+
// "actively reasoning" from "process wedged".
|
|
153726
|
+
//
|
|
153727
|
+
// Measured on a Terminal-Bench 4.0 trial (2026-08-30, deepseek-v4-pro
|
|
153728
|
+
// --effort max): nex-output.jsonl sat at 4 lines for 65 MINUTES while
|
|
153729
|
+
// tcpdump inside the container's netns showed ~25 packets/s still flowing
|
|
153730
|
+
// and the turn ultimately reported 310,872 output tokens. Nothing was
|
|
153731
|
+
// actually wrong — but every signal available to the harness (log line
|
|
153732
|
+
// count, file mtime) said "hung", and the socket/CPU forensics needed to
|
|
153733
|
+
// prove otherwise are not something a CI wrapper can do.
|
|
153734
|
+
//
|
|
153735
|
+
// `thinking_progress` is the load-bearing one: it carries the backend's
|
|
153736
|
+
// cumulative output-token count (routes/code.js's sendProgress, already
|
|
153737
|
+
// throttled to <=5/s server-side, so this cannot flood the log) and fires
|
|
153738
|
+
// DURING the phase. That makes it a real heartbeat.
|
|
153739
|
+
onThinkingProgress: (tokens) => {
|
|
153740
|
+
emit({ type: "thinking_progress", tokens });
|
|
153741
|
+
},
|
|
153742
|
+
// Fires once per turn at message_complete with the full reasoning text.
|
|
153743
|
+
// Typed `thinking` deliberately: the bench's ATIF converter (atif.py)
|
|
153744
|
+
// already routes exactly this event type into the trajectory's reasoning
|
|
153745
|
+
// buffer, so wiring it here also fills in reasoning that was previously
|
|
153746
|
+
// dropped on the floor for every headless run.
|
|
153747
|
+
onThinking: (text) => {
|
|
153748
|
+
emit({ type: "thinking", text });
|
|
153749
|
+
},
|
|
153750
|
+
// onThinkingDelta is intentionally NOT wired. Its chunks concatenate to the
|
|
153751
|
+
// same string `onThinking` emits in full above, so emitting both would
|
|
153752
|
+
// duplicate the entire reasoning trace — on the 310k-token turn measured
|
|
153753
|
+
// above that is megabytes of redundant NDJSON — while adding no liveness
|
|
153754
|
+
// signal `thinking_progress` does not already provide.
|
|
153667
153755
|
// System notice (mid-run auto-prune/auto-compact) — emit as its own event
|
|
153668
153756
|
// type instead of falling through to onText, so a stream-json consumer
|
|
153669
153757
|
// doesn't see compaction housekeeping text mixed into the model's `text`
|
|
@@ -153755,7 +153843,7 @@ function printHelp() {
|
|
|
153755
153843
|
["/help", "Show this help"],
|
|
153756
153844
|
["/model [name]", "Switch model (e.g. claude-opus-5, gpt-5.4)"],
|
|
153757
153845
|
["/mode [ask|edit|plan|auto]", "Set agent mode"],
|
|
153758
|
-
["/effort [
|
|
153846
|
+
["/effort [level]", "Set thinking effort level (options depend on the current model \u2014 run /effort with no argument to see them)"],
|
|
153759
153847
|
["/yolo", "Auto-approve all permissions"],
|
|
153760
153848
|
["/balance", "Show wallet balance"],
|
|
153761
153849
|
["/add <filepath>", "Add a file to conversation context"],
|
|
@@ -153876,8 +153964,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
|
|
|
153876
153964
|
let sessionId = crypto4.randomBytes(8).toString("hex");
|
|
153877
153965
|
let sessionTitle = "";
|
|
153878
153966
|
let agentMode = "auto";
|
|
153879
|
-
|
|
153880
|
-
let effortLevel = options.effort && VALID_EFFORT_LEVELS.includes(options.effort) ? options.effort : "medium";
|
|
153967
|
+
let effortLevel = options.effort ? clampEffortForModel(options.effort, modelAlias) : defaultEffortForModel(modelAlias);
|
|
153881
153968
|
const permRules = initPermissions(workDir);
|
|
153882
153969
|
const ruleCount = permRules.allow.length + permRules.ask.length + permRules.deny.length;
|
|
153883
153970
|
if (ruleCount && !headless)
|
|
@@ -154356,7 +154443,13 @@ ${dirList}`;
|
|
|
154356
154443
|
console.log(source_default.dim(` Options: ${SELECTABLE_MODELS.map(modelWithCostHint).join(" \xB7 ")}`));
|
|
154357
154444
|
} else if (SELECTABLE_MODELS.includes(normaliseModelId(requested))) {
|
|
154358
154445
|
modelAlias = normaliseModelId(requested);
|
|
154446
|
+
const clamped = clampEffortForModel(effortLevel, modelAlias);
|
|
154447
|
+
const effortChanged = clamped !== effortLevel;
|
|
154448
|
+
effortLevel = clamped;
|
|
154359
154449
|
console.log(source_default.green(` Model \u2192 ${source_default.bold(resolveModelLabel(modelAlias))}`));
|
|
154450
|
+
if (effortChanged) {
|
|
154451
|
+
console.log(source_default.dim(` Effort reset \u2192 ${effortLevel} (previous level not available on this model)`));
|
|
154452
|
+
}
|
|
154360
154453
|
} else {
|
|
154361
154454
|
console.log(source_default.red(` Unknown: ${requested}. Options: ${SELECTABLE_MODELS.join(", ")}`));
|
|
154362
154455
|
}
|
|
@@ -154383,13 +154476,15 @@ ${dirList}`;
|
|
|
154383
154476
|
}
|
|
154384
154477
|
case "/effort": {
|
|
154385
154478
|
const effortArg = arg.toLowerCase();
|
|
154479
|
+
const config = effortConfigFor(modelAlias);
|
|
154480
|
+
const optionsLine = config.levels.map((v, i2) => `${v} (${config.names[i2]})`).join(" \xB7 ");
|
|
154386
154481
|
if (!effortArg) {
|
|
154387
|
-
console.log(source_default.dim(` Current: ${source_default.cyan(effortLevel)} Options:
|
|
154388
|
-
} else if (
|
|
154482
|
+
console.log(source_default.dim(` Current: ${source_default.cyan(effortLevel)} Options: ${optionsLine}`));
|
|
154483
|
+
} else if (config.levels.includes(effortArg)) {
|
|
154389
154484
|
effortLevel = effortArg;
|
|
154390
154485
|
console.log(source_default.green(` Effort \u2192 ${source_default.bold(effortArg)}`));
|
|
154391
154486
|
} else {
|
|
154392
|
-
console.log(source_default.red(` Unknown: ${effortArg}`));
|
|
154487
|
+
console.log(source_default.red(` Unknown: ${effortArg}. Options: ${optionsLine}`));
|
|
154393
154488
|
}
|
|
154394
154489
|
rl.prompt();
|
|
154395
154490
|
return;
|
|
@@ -155151,7 +155246,7 @@ program2.command("sessions").description("List or delete saved chat sessions").o
|
|
|
155151
155246
|
console.log(source_default.dim(" nex sessions --delete <id> to remove one"));
|
|
155152
155247
|
console.log();
|
|
155153
155248
|
});
|
|
155154
|
-
program2.option("-p, --pro", "Use Claude Opus 5 (most capable)").option("-t, --turbo", "Use Claude Sonnet 5 (fast, default)").option("-u, --ultra", "Use Claude Fable 5 (most powerful)").option("-y, --yolo", "Auto-approve all tool calls (no permission prompts)").option("-d, --dir <path>", "Set working directory (default: current directory)").option("-m, --model <name>", "Explicit model, e.g. claude-opus-5 | gpt-5.4 | gpt-4.1").option("-r, --resume [id]", "Resume last session, or a specific session id").option("-e, --effort <level>", "Thinking effort: low
|
|
155249
|
+
program2.option("-p, --pro", "Use Claude Opus 5 (most capable)").option("-t, --turbo", "Use Claude Sonnet 5 (fast, default)").option("-u, --ultra", "Use Claude Fable 5 (most powerful)").option("-y, --yolo", "Auto-approve all tool calls (no permission prompts)").option("-d, --dir <path>", "Set working directory (default: current directory)").option("-m, --model <name>", "Explicit model, e.g. claude-opus-5 | gpt-5.4 | gpt-4.1").option("-r, --resume [id]", "Resume last session, or a specific session id").option("-e, --effort <level>", "Thinking effort \u2014 real levels depend on the model (e.g. DeepSeek: low|high|max, Qwen: low|high); run `nex --model <id>` then `/effort` with no argument to see a model's scale").option("--no-banner", "Skip the ASCII banner (useful in scripts/pipes)").option("--output-format <fmt>", "One-shot output format: text | json | stream-json (implies auto-approve)").option("--audit [path]", "Log every tool call (and refusal) to a JSONL trail (default: .nexrall/audit/<session>.jsonl)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
|
|
155155
155250
|
if (!(0, import_code_core7.isAuthenticated)()) {
|
|
155156
155251
|
console.error("Not logged in. Run: nex auth");
|
|
155157
155252
|
process.exit(1);
|
|
@@ -155186,8 +155281,9 @@ program2.option("-p, --pro", "Use Claude Opus 5 (most capable)").option("-t, --t
|
|
|
155186
155281
|
let effort;
|
|
155187
155282
|
if (options.effort) {
|
|
155188
155283
|
const lvl = String(options.effort).toLowerCase();
|
|
155189
|
-
|
|
155190
|
-
|
|
155284
|
+
const config = effortConfigFor(normaliseModelId(model));
|
|
155285
|
+
if (!config.levels.includes(lvl)) {
|
|
155286
|
+
console.error(`Invalid --effort "${options.effort}" for model "${model}". Use: ${config.levels.join(" | ")}`);
|
|
155191
155287
|
process.exit(1);
|
|
155192
155288
|
}
|
|
155193
155289
|
effort = lvl;
|