nexrall-code 0.5.58 → 0.5.61
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 +321 -113
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -12663,6 +12663,34 @@ ${doc}` : sig || "No type information." };
|
|
|
12663
12663
|
}
|
|
12664
12664
|
});
|
|
12665
12665
|
|
|
12666
|
+
// ../core/dist/util/safeSlice.js
|
|
12667
|
+
var require_safeSlice = __commonJS({
|
|
12668
|
+
"../core/dist/util/safeSlice.js"(exports) {
|
|
12669
|
+
"use strict";
|
|
12670
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12671
|
+
exports.sliceSafeEnd = sliceSafeEnd;
|
|
12672
|
+
exports.sliceSafeStart = sliceSafeStart;
|
|
12673
|
+
function sliceSafeEnd(s2, max) {
|
|
12674
|
+
if (s2.length <= max)
|
|
12675
|
+
return s2;
|
|
12676
|
+
let end = max;
|
|
12677
|
+
const code = s2.charCodeAt(end - 1);
|
|
12678
|
+
if (code >= 55296 && code <= 56319)
|
|
12679
|
+
end--;
|
|
12680
|
+
return s2.slice(0, end);
|
|
12681
|
+
}
|
|
12682
|
+
function sliceSafeStart(s2, from) {
|
|
12683
|
+
if (from <= 0)
|
|
12684
|
+
return s2;
|
|
12685
|
+
let start = from;
|
|
12686
|
+
const code = s2.charCodeAt(start);
|
|
12687
|
+
if (code >= 56320 && code <= 57343)
|
|
12688
|
+
start++;
|
|
12689
|
+
return s2.slice(start);
|
|
12690
|
+
}
|
|
12691
|
+
}
|
|
12692
|
+
});
|
|
12693
|
+
|
|
12666
12694
|
// ../core/dist/tools/executor.js
|
|
12667
12695
|
var require_executor = __commonJS({
|
|
12668
12696
|
"../core/dist/tools/executor.js"(exports) {
|
|
@@ -12731,6 +12759,7 @@ var require_executor = __commonJS({
|
|
|
12731
12759
|
var client_1 = require_client();
|
|
12732
12760
|
var symbols_1 = require_symbols();
|
|
12733
12761
|
var tsLangService_1 = require_tsLangService();
|
|
12762
|
+
var safeSlice_1 = require_safeSlice();
|
|
12734
12763
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
12735
12764
|
var MAX_FETCH_BYTES = 200 * 1024;
|
|
12736
12765
|
var MAX_READ_BYTES = 500 * 1024;
|
|
@@ -13024,7 +13053,7 @@ Resolve the conflict (remove <<<<<<< / ======= / >>>>>>> markers and keep the in
|
|
|
13024
13053
|
}
|
|
13025
13054
|
let output = lines.join("\n");
|
|
13026
13055
|
if (output.length > MAX_OUTPUT_CHARS) {
|
|
13027
|
-
output =
|
|
13056
|
+
output = (0, safeSlice_1.sliceSafeEnd)(output, MAX_OUTPUT_CHARS) + `
|
|
13028
13057
|
[Truncated \u2014 ${entries.length} total entries]`;
|
|
13029
13058
|
}
|
|
13030
13059
|
return { output };
|
|
@@ -13496,7 +13525,7 @@ Update these call-sites (or restore the symbol), then run a build/typecheck to c
|
|
|
13496
13525
|
return { output: "No matching files found." };
|
|
13497
13526
|
let out = matches.join("\n");
|
|
13498
13527
|
if (out.length > MAX_OUTPUT_CHARS)
|
|
13499
|
-
out =
|
|
13528
|
+
out = (0, safeSlice_1.sliceSafeEnd)(out, MAX_OUTPUT_CHARS) + "\n[Truncated]";
|
|
13500
13529
|
return { output: out };
|
|
13501
13530
|
} else {
|
|
13502
13531
|
const SEARCH_MAX_BUFFER = 64 * 1024 * 1024;
|
|
@@ -13566,7 +13595,7 @@ ${globalCapMatches(output)}` : "";
|
|
|
13566
13595
|
[Showing first ${GLOBAL_MATCH_CAP} of ${lines.length} matched lines \u2014 narrow the pattern/path for the rest.]`;
|
|
13567
13596
|
}
|
|
13568
13597
|
if (capped.length > MAX_OUTPUT_CHARS) {
|
|
13569
|
-
capped =
|
|
13598
|
+
capped = (0, safeSlice_1.sliceSafeEnd)(capped, MAX_OUTPUT_CHARS);
|
|
13570
13599
|
note = `
|
|
13571
13600
|
[Output truncated at ${MAX_OUTPUT_CHARS / 1024}KB.]`;
|
|
13572
13601
|
}
|
|
@@ -14102,7 +14131,7 @@ ${d}`).join("\n") + xfile + sec
|
|
|
14102
14131
|
return { output: `No files matched pattern: ${pattern}` };
|
|
14103
14132
|
let out = matches.join("\n");
|
|
14104
14133
|
if (out.length > MAX_OUTPUT_CHARS)
|
|
14105
|
-
out =
|
|
14134
|
+
out = (0, safeSlice_1.sliceSafeEnd)(out, MAX_OUTPUT_CHARS) + "\n[Truncated]";
|
|
14106
14135
|
return { output: `${matches.length} file(s) matched "${pattern}":
|
|
14107
14136
|
${out}` };
|
|
14108
14137
|
} catch (err) {
|
|
@@ -14158,7 +14187,7 @@ ${lines.join("\n")}`;
|
|
|
14158
14187
|
switch (out.output_type) {
|
|
14159
14188
|
case "stream": {
|
|
14160
14189
|
const txt = nbJoin(out.text ?? "");
|
|
14161
|
-
lines.push(`[${out.name === "stderr" ? "stderr" : "stdout"}] ${
|
|
14190
|
+
lines.push(`[${out.name === "stderr" ? "stderr" : "stdout"}] ${(0, safeSlice_1.sliceSafeEnd)(txt, 3e3)}${txt.length > 3e3 ? "\n[truncated]" : ""}`);
|
|
14162
14191
|
break;
|
|
14163
14192
|
}
|
|
14164
14193
|
case "execute_result":
|
|
@@ -14168,9 +14197,9 @@ ${lines.join("\n")}`;
|
|
|
14168
14197
|
const html = d["text/html"] ? nbJoin(d["text/html"]) : null;
|
|
14169
14198
|
const hasImg = "image/png" in d || "image/jpeg" in d || "image/svg+xml" in d;
|
|
14170
14199
|
if (plain)
|
|
14171
|
-
lines.push(
|
|
14200
|
+
lines.push((0, safeSlice_1.sliceSafeEnd)(plain, 2e3) + (plain.length > 2e3 ? "\n[truncated]" : ""));
|
|
14172
14201
|
else if (html)
|
|
14173
|
-
lines.push(stripHtml(html)
|
|
14202
|
+
lines.push((0, safeSlice_1.sliceSafeEnd)(stripHtml(html), 1e3));
|
|
14174
14203
|
if (hasImg)
|
|
14175
14204
|
lines.push("[image output \u2014 not shown in text mode]");
|
|
14176
14205
|
break;
|
|
@@ -14179,7 +14208,7 @@ ${lines.join("\n")}`;
|
|
|
14179
14208
|
lines.push(`[error] ${out.ename ?? "Error"}: ${out.evalue ?? ""}`);
|
|
14180
14209
|
if (out.traceback) {
|
|
14181
14210
|
const clean = out.traceback.map((l) => l.replace(/\x1b\[[0-9;]*[mGKH]/g, "")).join("\n");
|
|
14182
|
-
lines.push(
|
|
14211
|
+
lines.push((0, safeSlice_1.sliceSafeEnd)(clean, 1500) + (clean.length > 1500 ? "\n[truncated]" : ""));
|
|
14183
14212
|
}
|
|
14184
14213
|
break;
|
|
14185
14214
|
}
|
|
@@ -14222,7 +14251,7 @@ ${lines.join("\n")}`;
|
|
|
14222
14251
|
});
|
|
14223
14252
|
let output = lines.join("\n");
|
|
14224
14253
|
if (output.length > MAX_OUTPUT_CHARS) {
|
|
14225
|
-
output =
|
|
14254
|
+
output = (0, safeSlice_1.sliceSafeEnd)(output, MAX_OUTPUT_CHARS) + "\n\n[Notebook output truncated at 100KB]";
|
|
14226
14255
|
}
|
|
14227
14256
|
return { output };
|
|
14228
14257
|
} catch (err) {
|
|
@@ -14733,9 +14762,17 @@ var require_agentTypes = __commonJS({
|
|
|
14733
14762
|
//
|
|
14734
14763
|
// `tools` is deliberately UNDEFINED, which means "no allowlist" — full access,
|
|
14735
14764
|
// inheriting whatever the session permits. That is the same power an unnamed
|
|
14736
|
-
// sub-task always had; naming it changes only who can see and deny it.
|
|
14737
|
-
//
|
|
14738
|
-
//
|
|
14765
|
+
// sub-task always had; naming it changes only who can see and deny it.
|
|
14766
|
+
//
|
|
14767
|
+
// It also makes this the ONLY builtin that can nest, since every specialist omits
|
|
14768
|
+
// `task` on purpose. So the tree this enables is up to `maxSubagentDepth` generations
|
|
14769
|
+
// of unrestricted agents — accepted deliberately, because the alternative (a catch-all
|
|
14770
|
+
// that cannot delegate) removes the one agent suited to orchestration.
|
|
14771
|
+
//
|
|
14772
|
+
// The safety properties that bound it: plan mode is inherited, the user's permission
|
|
14773
|
+
// gate runs on every call, a child's allowlist is INTERSECTED with its parent's so a
|
|
14774
|
+
// restricted ancestor cannot be escaped through this agent, `test_files_only` is
|
|
14775
|
+
// likewise sticky, and both the depth ceiling and the session budget still apply.
|
|
14739
14776
|
name: "general-purpose",
|
|
14740
14777
|
description: "General-purpose worker for a multi-step task that needs BOTH exploration and changes (edit files, run commands) and that no specialist above fits. Inherits the session model and full tool access, so prefer a narrower agent when one matches.",
|
|
14741
14778
|
prompt: "You are a general-purpose engineering sub-agent. Work the task end to end: explore what you need, make the changes, and verify them with the project's own build/test commands.\n\nRules:\n- Mirror existing conventions; make the smallest correct change.\n- Verify before you claim success. If you could not verify, say so explicitly.\n- Your FINAL MESSAGE is the only thing that reaches the main agent: state what you changed (with file paths), what you ran and its outcome, and anything you deliberately left undone.",
|
|
@@ -14968,7 +15005,19 @@ var require_agentTypes = __commonJS({
|
|
|
14968
15005
|
"testfilesonly",
|
|
14969
15006
|
"memory"
|
|
14970
15007
|
]);
|
|
14971
|
-
var VALID_MODELS = [
|
|
15008
|
+
var VALID_MODELS = [
|
|
15009
|
+
"claude-sonnet-5",
|
|
15010
|
+
"claude-opus-5",
|
|
15011
|
+
"claude-fable-5",
|
|
15012
|
+
"gpt-5.4",
|
|
15013
|
+
"gpt-5.4-mini",
|
|
15014
|
+
"gpt-4.1",
|
|
15015
|
+
// Legacy tier aliases — still accepted, still resolved by the backend.
|
|
15016
|
+
"turbo",
|
|
15017
|
+
"pro",
|
|
15018
|
+
"ultra",
|
|
15019
|
+
"fast"
|
|
15020
|
+
];
|
|
14972
15021
|
function parseFrontmatter(raw) {
|
|
14973
15022
|
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
14974
15023
|
if (!m2)
|
|
@@ -14982,8 +15031,13 @@ var require_agentTypes = __commonJS({
|
|
|
14982
15031
|
return { meta, body: (m2[2] ?? "").trim(), ok: true };
|
|
14983
15032
|
}
|
|
14984
15033
|
function parseModel(v) {
|
|
14985
|
-
const
|
|
14986
|
-
|
|
15034
|
+
const raw = (v ?? "").trim();
|
|
15035
|
+
if (!raw)
|
|
15036
|
+
return void 0;
|
|
15037
|
+
const lower2 = raw.toLowerCase();
|
|
15038
|
+
if (lower2 === "turbo" || lower2 === "pro" || lower2 === "ultra" || lower2 === "fast")
|
|
15039
|
+
return lower2;
|
|
15040
|
+
return raw;
|
|
14987
15041
|
}
|
|
14988
15042
|
function parseBool(v) {
|
|
14989
15043
|
return /^(true|yes|1|on)$/i.test((v ?? "").trim());
|
|
@@ -15046,11 +15100,12 @@ var require_agentTypes = __commonJS({
|
|
|
15046
15100
|
message: `has memory: "${meta.memory}", which is not a valid scope \u2014 use one of ${VALID_MEMORY_SCOPES.join(", ")}, or omit the line to give this agent no persistent notes.`
|
|
15047
15101
|
});
|
|
15048
15102
|
}
|
|
15049
|
-
|
|
15103
|
+
const declaredModel = meta.model === void 0 ? void 0 : parseModel(meta.model);
|
|
15104
|
+
if (meta.model !== void 0 && (declaredModel === void 0 || !VALID_MODELS.includes(declaredModel))) {
|
|
15050
15105
|
warnings.push({
|
|
15051
15106
|
file: full,
|
|
15052
15107
|
agent: name,
|
|
15053
|
-
message: `has model: "${meta.model}", which
|
|
15108
|
+
message: `has model: "${meta.model}", which this version does not recognise \u2014 expected one of ${VALID_MODELS.join(", ")}, or omit the line to inherit the current session's model. It will still be sent; the server decides whether it is valid.`
|
|
15054
15109
|
});
|
|
15055
15110
|
}
|
|
15056
15111
|
const unknown = (tools ?? []).filter((t2) => !KNOWN_TOOL_NAMES.has(t2) && !t2.includes("__"));
|
|
@@ -15061,11 +15116,11 @@ var require_agentTypes = __commonJS({
|
|
|
15061
15116
|
message: `lists unknown tool name(s): ${unknown.join(", ")}. An allowlist only grants, so these silently do nothing and the agent cannot use them. Tool names are lower_snake_case (read_file, search_files, glob, bash).`
|
|
15062
15117
|
});
|
|
15063
15118
|
}
|
|
15064
|
-
if ((tools ?? []).
|
|
15119
|
+
if ((tools ?? []).length === 1 && tools?.[0] === "task") {
|
|
15065
15120
|
warnings.push({
|
|
15066
15121
|
file: full,
|
|
15067
15122
|
agent: name,
|
|
15068
|
-
message: "lists `task
|
|
15123
|
+
message: "lists `task` as its ONLY tool, so it can delegate but cannot read, write or run anything itself \u2014 it has no way to do or verify work. Add the tools it needs, or drop the `tools:` line to inherit its parent's."
|
|
15069
15124
|
});
|
|
15070
15125
|
}
|
|
15071
15126
|
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
|
@@ -15704,10 +15759,12 @@ var require_agentRegistry = __commonJS({
|
|
|
15704
15759
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15705
15760
|
exports._limits = void 0;
|
|
15706
15761
|
exports.rememberAgent = rememberAgent;
|
|
15762
|
+
exports.canResume = canResume;
|
|
15707
15763
|
exports.updateAgent = updateAgent;
|
|
15708
15764
|
exports.getAgent = getAgent;
|
|
15709
15765
|
exports.listAgents = listAgents;
|
|
15710
15766
|
exports._resetAgentRegistry = _resetAgentRegistry;
|
|
15767
|
+
var node_crypto_1 = __require("node:crypto");
|
|
15711
15768
|
var MAX_AGENTS = 20;
|
|
15712
15769
|
var MAX_TOTAL_BYTES = 8 * 1024 * 1024;
|
|
15713
15770
|
var _agents = /* @__PURE__ */ new Map();
|
|
@@ -15740,12 +15797,13 @@ var require_agentRegistry = __commonJS({
|
|
|
15740
15797
|
return MAX_TOTAL_BYTES;
|
|
15741
15798
|
}
|
|
15742
15799
|
}
|
|
15743
|
-
function rememberAgent(agentName, description, messages) {
|
|
15744
|
-
const id = `agent_${++_counter}`;
|
|
15800
|
+
function rememberAgent(agentName, description, messages, ownerScope = "root") {
|
|
15801
|
+
const id = `agent_${++_counter}_${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
|
|
15745
15802
|
const now = Date.now();
|
|
15746
15803
|
_agents.set(id, {
|
|
15747
15804
|
id,
|
|
15748
15805
|
agentName,
|
|
15806
|
+
ownerScope,
|
|
15749
15807
|
description,
|
|
15750
15808
|
messages,
|
|
15751
15809
|
createdAt: now,
|
|
@@ -15756,10 +15814,15 @@ var require_agentRegistry = __commonJS({
|
|
|
15756
15814
|
evictUntilWithinLimits();
|
|
15757
15815
|
return id;
|
|
15758
15816
|
}
|
|
15759
|
-
function
|
|
15817
|
+
function canResume(stored, scope) {
|
|
15818
|
+
return stored.ownerScope === scope || scope === "root";
|
|
15819
|
+
}
|
|
15820
|
+
function updateAgent(id, messages, scope = "root") {
|
|
15760
15821
|
const existing = _agents.get(id);
|
|
15761
15822
|
if (!existing)
|
|
15762
15823
|
return;
|
|
15824
|
+
if (!canResume(existing, scope))
|
|
15825
|
+
return;
|
|
15763
15826
|
existing.messages = messages;
|
|
15764
15827
|
existing.updatedAt = Date.now();
|
|
15765
15828
|
existing.seq = ++_seq;
|
|
@@ -15961,15 +16024,21 @@ var require_loop = __commonJS({
|
|
|
15961
16024
|
};
|
|
15962
16025
|
}();
|
|
15963
16026
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15964
|
-
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = exports.ToolNotAllowedError = exports.
|
|
16027
|
+
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = exports.ToolNotAllowedError = exports.AGENT_MEMORY_TOOL_SCHEMA = exports.AGENT_MEMORY_TOOL = exports._stallLimits = exports.bashNeedsRepoLock = void 0;
|
|
15965
16028
|
exports.errorRoundSignature = errorRoundSignature;
|
|
15966
16029
|
exports.executeAgentMemoryWrite = executeAgentMemoryWrite;
|
|
15967
16030
|
exports.stopReasonNotice = stopReasonNotice;
|
|
15968
16031
|
exports.resolveMaxIterations = resolveMaxIterations;
|
|
15969
16032
|
exports.lockPathsFor = lockPathsFor;
|
|
15970
16033
|
exports.resolveMaxConcurrentSubtasks = resolveMaxConcurrentSubtasks;
|
|
16034
|
+
exports.resolveMaxSubagentsPerSession = resolveMaxSubagentsPerSession;
|
|
15971
16035
|
exports.createLimiter = createLimiter;
|
|
15972
16036
|
exports._resetSubTaskLimiter = _resetSubTaskLimiter;
|
|
16037
|
+
exports.resetSessionSubAgentBudget = resetSessionSubAgentBudget2;
|
|
16038
|
+
exports._sessionSubAgentCount = _sessionSubAgentCount;
|
|
16039
|
+
exports.resolveMaxSubagentDepth = resolveMaxSubagentDepth;
|
|
16040
|
+
exports.noSpawnReason = noSpawnReason;
|
|
16041
|
+
exports.intersectAllowlists = intersectAllowlists;
|
|
15973
16042
|
exports.canSpawnSubAgents = canSpawnSubAgents;
|
|
15974
16043
|
exports.resolveSubtaskTimeoutMs = resolveSubtaskTimeoutMs;
|
|
15975
16044
|
exports.extractSubTaskText = extractSubTaskText;
|
|
@@ -16008,6 +16077,7 @@ var require_loop = __commonJS({
|
|
|
16008
16077
|
var flaky_1 = require_flaky();
|
|
16009
16078
|
var claimEvidence_1 = require_claimEvidence();
|
|
16010
16079
|
var memory_1 = require_memory();
|
|
16080
|
+
var safeSlice_1 = require_safeSlice();
|
|
16011
16081
|
var fs9 = __importStar(__require("fs"));
|
|
16012
16082
|
var path6 = __importStar(__require("path"));
|
|
16013
16083
|
var child_process_1 = __require("child_process");
|
|
@@ -16242,7 +16312,7 @@ ${ctx.repeatError}
|
|
|
16242
16312
|
}
|
|
16243
16313
|
var DEFAULT_MAX_CONCURRENT_SUBTASKS = 4;
|
|
16244
16314
|
function resolveMaxConcurrentSubtasks(settingsRaw = {}) {
|
|
16245
|
-
const clamp = (n) => Math.min(Math.floor(n), 16);
|
|
16315
|
+
const clamp = (n) => Math.max(1, Math.min(Math.floor(n), 16));
|
|
16246
16316
|
const fromEnv = Number(process.env.NEXRALL_MAX_CONCURRENT_SUBTASKS);
|
|
16247
16317
|
if (Number.isFinite(fromEnv) && fromEnv > 0)
|
|
16248
16318
|
return clamp(fromEnv);
|
|
@@ -16251,6 +16321,17 @@ ${ctx.repeatError}
|
|
|
16251
16321
|
return clamp(fromSettings);
|
|
16252
16322
|
return DEFAULT_MAX_CONCURRENT_SUBTASKS;
|
|
16253
16323
|
}
|
|
16324
|
+
var DEFAULT_MAX_SUBAGENTS_PER_SESSION = 100;
|
|
16325
|
+
function resolveMaxSubagentsPerSession(settingsRaw = {}) {
|
|
16326
|
+
const floor = (n) => Math.max(1, Math.min(Math.floor(n), 1e4));
|
|
16327
|
+
const fromEnv = Number(process.env.NEXRALL_MAX_SUBAGENTS_PER_SESSION);
|
|
16328
|
+
if (Number.isFinite(fromEnv) && fromEnv > 0)
|
|
16329
|
+
return floor(fromEnv);
|
|
16330
|
+
const fromSettings = Number(settingsRaw.maxSubagentsPerSession);
|
|
16331
|
+
if (Number.isFinite(fromSettings) && fromSettings > 0)
|
|
16332
|
+
return floor(fromSettings);
|
|
16333
|
+
return DEFAULT_MAX_SUBAGENTS_PER_SESSION;
|
|
16334
|
+
}
|
|
16254
16335
|
function createLimiter(max) {
|
|
16255
16336
|
let active = 0;
|
|
16256
16337
|
const queue = [];
|
|
@@ -16269,19 +16350,61 @@ ${ctx.repeatError}
|
|
|
16269
16350
|
}
|
|
16270
16351
|
};
|
|
16271
16352
|
}
|
|
16272
|
-
var
|
|
16353
|
+
var _subTaskLimiters = /* @__PURE__ */ new Map();
|
|
16354
|
+
var _subTaskLimitMaxByDepth = /* @__PURE__ */ new Map();
|
|
16273
16355
|
var _subTaskLimitMax = 0;
|
|
16274
|
-
var
|
|
16275
|
-
function subTaskLimiter(workDir) {
|
|
16276
|
-
if (!
|
|
16356
|
+
var _inFlightByDepth = /* @__PURE__ */ new Map();
|
|
16357
|
+
function subTaskLimiter(depth, workDir) {
|
|
16358
|
+
if (!_subTaskLimitMax) {
|
|
16277
16359
|
_subTaskLimitMax = resolveMaxConcurrentSubtasks(workDir ? (0, rules_1.loadSettings)(workDir).raw : {});
|
|
16278
|
-
_subTaskLimitInstance = createLimiter(_subTaskLimitMax);
|
|
16279
16360
|
}
|
|
16280
|
-
|
|
16361
|
+
let run = _subTaskLimiters.get(depth);
|
|
16362
|
+
let max = _subTaskLimitMaxByDepth.get(depth) ?? 0;
|
|
16363
|
+
if (!run) {
|
|
16364
|
+
max = Math.max(1, Math.ceil(_subTaskLimitMax / 2 ** Math.max(0, depth - 1)));
|
|
16365
|
+
run = createLimiter(max);
|
|
16366
|
+
_subTaskLimiters.set(depth, run);
|
|
16367
|
+
_subTaskLimitMaxByDepth.set(depth, max);
|
|
16368
|
+
}
|
|
16369
|
+
return { run, max };
|
|
16281
16370
|
}
|
|
16282
16371
|
function _resetSubTaskLimiter() {
|
|
16283
|
-
|
|
16372
|
+
_subTaskLimiters.clear();
|
|
16373
|
+
_subTaskLimitMaxByDepth.clear();
|
|
16374
|
+
_inFlightByDepth.clear();
|
|
16284
16375
|
_subTaskLimitMax = 0;
|
|
16376
|
+
_subAgentsThisSession = 0;
|
|
16377
|
+
_sessionCapMax = 0;
|
|
16378
|
+
_sessionCapNotified = false;
|
|
16379
|
+
}
|
|
16380
|
+
var _subAgentsThisSession = 0;
|
|
16381
|
+
var _sessionCapMax = 0;
|
|
16382
|
+
var _sessionCapNotified = false;
|
|
16383
|
+
function resetSessionSubAgentBudget2() {
|
|
16384
|
+
_subAgentsThisSession = 0;
|
|
16385
|
+
_sessionCapMax = 0;
|
|
16386
|
+
_sessionCapNotified = false;
|
|
16387
|
+
}
|
|
16388
|
+
function claimSessionSubAgentSlot(workDir, notify) {
|
|
16389
|
+
if (!_sessionCapMax) {
|
|
16390
|
+
_sessionCapMax = resolveMaxSubagentsPerSession(workDir ? (0, rules_1.loadSettings)(workDir).raw : {});
|
|
16391
|
+
}
|
|
16392
|
+
if (_subAgentsThisSession >= _sessionCapMax) {
|
|
16393
|
+
if (!_sessionCapNotified) {
|
|
16394
|
+
_sessionCapNotified = true;
|
|
16395
|
+
notify?.(`\u26A0\uFE0F Sub-agent budget reached (${_sessionCapMax} this session) \u2014 further delegation is blocked and the agent will continue without it. Raise "maxSubagentsPerSession" in .nexrall/settings.json if this was legitimate work.`);
|
|
16396
|
+
}
|
|
16397
|
+
return `Sub-agent budget for this session is exhausted (${_sessionCapMax} started). This is a runaway-delegation guard, not a per-task limit: do the remaining work directly, and say in your final message that you hit the delegation cap.`;
|
|
16398
|
+
}
|
|
16399
|
+
_subAgentsThisSession++;
|
|
16400
|
+
return null;
|
|
16401
|
+
}
|
|
16402
|
+
function refundSessionSubAgentSlot() {
|
|
16403
|
+
if (_subAgentsThisSession > 0)
|
|
16404
|
+
_subAgentsThisSession--;
|
|
16405
|
+
}
|
|
16406
|
+
function _sessionSubAgentCount() {
|
|
16407
|
+
return _subAgentsThisSession;
|
|
16285
16408
|
}
|
|
16286
16409
|
function humanDescription(name, input) {
|
|
16287
16410
|
switch (name) {
|
|
@@ -16364,10 +16487,33 @@ ${ctx.repeatError}
|
|
|
16364
16487
|
return `Use tool: ${name}`;
|
|
16365
16488
|
}
|
|
16366
16489
|
}
|
|
16367
|
-
var
|
|
16368
|
-
|
|
16369
|
-
function
|
|
16370
|
-
|
|
16490
|
+
var DEFAULT_MAX_TASK_DEPTH = 3;
|
|
16491
|
+
var HARD_MAX_TASK_DEPTH = 5;
|
|
16492
|
+
function resolveMaxSubagentDepth(settingsRaw = {}) {
|
|
16493
|
+
const clamp = (n) => Math.max(1, Math.min(Math.floor(n), HARD_MAX_TASK_DEPTH));
|
|
16494
|
+
const fromEnv = Number(process.env.NEXRALL_MAX_SUBAGENT_DEPTH);
|
|
16495
|
+
if (Number.isFinite(fromEnv) && fromEnv > 0)
|
|
16496
|
+
return clamp(fromEnv);
|
|
16497
|
+
const fromSettings = Number(settingsRaw.maxSubagentDepth);
|
|
16498
|
+
if (Number.isFinite(fromSettings) && fromSettings > 0)
|
|
16499
|
+
return clamp(fromSettings);
|
|
16500
|
+
return DEFAULT_MAX_TASK_DEPTH;
|
|
16501
|
+
}
|
|
16502
|
+
function noSpawnReason(kind, limit) {
|
|
16503
|
+
if (kind === "denied") {
|
|
16504
|
+
return "This sub-agent's definition does not grant `task`, so it may not delegate. That is a deliberate restriction on this agent type \u2014 do not ask for approval and do not look for a way around it. Do the work with the tools you have, or report back what is missing.";
|
|
16505
|
+
}
|
|
16506
|
+
return `Maximum sub-agent nesting depth (${limit ?? DEFAULT_MAX_TASK_DEPTH}) reached, so this run is a leaf and cannot delegate further. Depth is a budget, not a bug: finish this work yourself, or report back so a shallower frame can decide. (The ceiling is "maxSubagentDepth" in .nexrall/settings.json, but raising it mid-task will not help you \u2014 it applies from the next session.)`;
|
|
16507
|
+
}
|
|
16508
|
+
function intersectAllowlists(own, parent) {
|
|
16509
|
+
if (own && parent)
|
|
16510
|
+
return new Set([...own].filter((t2) => parent.has(t2)));
|
|
16511
|
+
if (own)
|
|
16512
|
+
return own;
|
|
16513
|
+
return parent ? new Set(parent) : null;
|
|
16514
|
+
}
|
|
16515
|
+
function canSpawnSubAgents(depth, allowedTools, limit = DEFAULT_MAX_TASK_DEPTH) {
|
|
16516
|
+
if (depth >= limit)
|
|
16371
16517
|
return false;
|
|
16372
16518
|
return allowedTools ? allowedTools.has("task") : true;
|
|
16373
16519
|
}
|
|
@@ -16391,24 +16537,6 @@ ${ctx.repeatError}
|
|
|
16391
16537
|
}
|
|
16392
16538
|
};
|
|
16393
16539
|
exports.ToolNotAllowedError = ToolNotAllowedError;
|
|
16394
|
-
function sliceSafeEnd(s2, max) {
|
|
16395
|
-
if (s2.length <= max)
|
|
16396
|
-
return s2;
|
|
16397
|
-
let end = max;
|
|
16398
|
-
const code = s2.charCodeAt(end - 1);
|
|
16399
|
-
if (code >= 55296 && code <= 56319)
|
|
16400
|
-
end--;
|
|
16401
|
-
return s2.slice(0, end);
|
|
16402
|
-
}
|
|
16403
|
-
function sliceSafeStart(s2, from) {
|
|
16404
|
-
if (from <= 0)
|
|
16405
|
-
return s2;
|
|
16406
|
-
let start = from;
|
|
16407
|
-
const code = s2.charCodeAt(start);
|
|
16408
|
-
if (code >= 56320 && code <= 57343)
|
|
16409
|
-
start++;
|
|
16410
|
-
return s2.slice(start);
|
|
16411
|
-
}
|
|
16412
16540
|
function extractSubTaskText(messages, preferLast = true) {
|
|
16413
16541
|
const assistants = messages.filter((m2) => m2.role === "assistant");
|
|
16414
16542
|
const textOf = (m2) => (m2?.content ?? []).filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("").trim();
|
|
@@ -16419,8 +16547,8 @@ ${ctx.repeatError}
|
|
|
16419
16547
|
function capSubTaskText(text, max = SUBTASK_MAX) {
|
|
16420
16548
|
if (text.length <= max)
|
|
16421
16549
|
return text;
|
|
16422
|
-
const head = sliceSafeEnd(text, Math.floor(max * 0.6));
|
|
16423
|
-
const tail = sliceSafeStart(text, text.length - Math.floor(max * 0.4));
|
|
16550
|
+
const head = (0, safeSlice_1.sliceSafeEnd)(text, Math.floor(max * 0.6));
|
|
16551
|
+
const tail = (0, safeSlice_1.sliceSafeStart)(text, text.length - Math.floor(max * 0.4));
|
|
16424
16552
|
return `${head}
|
|
16425
16553
|
|
|
16426
16554
|
[\u2026 sub-task output truncated (${text.length} chars) \u2014 kept the beginning and end \u2026]
|
|
@@ -16476,7 +16604,7 @@ ${recentFindings}` : header;
|
|
|
16476
16604
|
if (!text)
|
|
16477
16605
|
continue;
|
|
16478
16606
|
const name = nameById.get(String(b.tool_use_id ?? "")) ?? "tool";
|
|
16479
|
-
const body = text.length > maxChars ? `${sliceSafeEnd(text, maxChars)}
|
|
16607
|
+
const body = text.length > maxChars ? `${(0, safeSlice_1.sliceSafeEnd)(text, maxChars)}
|
|
16480
16608
|
\u2026 [truncated]` : text;
|
|
16481
16609
|
out.push(`\u2022 ${name}:
|
|
16482
16610
|
${body}`);
|
|
@@ -16493,29 +16621,35 @@ ${body}`);
|
|
|
16493
16621
|
}
|
|
16494
16622
|
return "";
|
|
16495
16623
|
}
|
|
16496
|
-
async function runSubTask(input, options, agentTypes) {
|
|
16624
|
+
async function runSubTask(input, options, agentTypes, started) {
|
|
16497
16625
|
const prompt2 = typeof input.prompt === "string" ? input.prompt.trim() : "";
|
|
16498
16626
|
if (!prompt2)
|
|
16499
16627
|
return { error: "task tool requires a non-empty prompt" };
|
|
16628
|
+
const agentScope = options._agentScope ?? "root";
|
|
16500
16629
|
const depth = options._depth ?? 0;
|
|
16501
|
-
|
|
16502
|
-
|
|
16630
|
+
const depthLimit = resolveMaxSubagentDepth(options.workDir ? (0, rules_1.loadSettings)(options.workDir).raw : {});
|
|
16631
|
+
if (depth >= depthLimit) {
|
|
16632
|
+
return { error: noSpawnReason("depth", depthLimit) };
|
|
16503
16633
|
}
|
|
16504
16634
|
const resumeId = typeof input.resume_agent_id === "string" ? input.resume_agent_id.trim() : "";
|
|
16505
16635
|
const resumed = resumeId ? (0, agentRegistry_1.getAgent)(resumeId) : void 0;
|
|
16636
|
+
if (resumed && !(0, agentRegistry_1.canResume)(resumed, agentScope)) {
|
|
16637
|
+
return {
|
|
16638
|
+
error: `No resumable sub-agent with id "${resumeId}" is available to this run. Start a fresh sub-task with a self-contained prompt instead.`
|
|
16639
|
+
};
|
|
16640
|
+
}
|
|
16506
16641
|
if (resumeId && !resumed) {
|
|
16507
16642
|
return {
|
|
16508
16643
|
error: `No resumable sub-agent with id "${resumeId}". Ids live only for the current session and the oldest are dropped when too many accumulate, so this one has expired or never existed. Start a fresh sub-task with a self-contained prompt instead.`
|
|
16509
16644
|
};
|
|
16510
16645
|
}
|
|
16511
16646
|
const requestedType = resumed ? resumed.agentName ?? "" : typeof input.subagent_type === "string" ? input.subagent_type : "";
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
}
|
|
16647
|
+
const denyKey = requestedType || "general-purpose";
|
|
16648
|
+
const decision = (0, rules_1.evaluatePermission)((0, rules_1.loadSettings)(options.workDir).permissions, "task", { subagent_type: denyKey }, options.workDir);
|
|
16649
|
+
if (decision === "deny") {
|
|
16650
|
+
return {
|
|
16651
|
+
error: `The sub-agent "${denyKey}" is disabled by a permission rule in this project (permissions.deny in settings.json). This is a deliberate policy choice, not a missing file \u2014 do not create it and do not retry. Do the work yourself, or use a different sub-agent.`
|
|
16652
|
+
};
|
|
16519
16653
|
}
|
|
16520
16654
|
let agent = (0, agentTypes_1.findAgentType)(agentTypes, requestedType);
|
|
16521
16655
|
let knownTypes = agentTypes;
|
|
@@ -16543,21 +16677,31 @@ ${agentMemoryNotes}` : "") + (inheritedMd ? `
|
|
|
16543
16677
|
---
|
|
16544
16678
|
|
|
16545
16679
|
${inheritedMd}` : "") : options.nexrallMd;
|
|
16546
|
-
const
|
|
16680
|
+
const testFilesOnly = !!agent?.testFilesOnly || !!options._testFilesOnly;
|
|
16681
|
+
const allowed = intersectAllowlists(agent?.tools ? new Set(agent.tools) : null, options._allowedTools);
|
|
16547
16682
|
if (allowed && memoryScope)
|
|
16548
16683
|
allowed.add(exports.AGENT_MEMORY_TOOL);
|
|
16549
16684
|
const gatedPermission = async (req) => {
|
|
16550
16685
|
if (req.tool === exports.AGENT_MEMORY_TOOL && !(agent && memoryScope)) {
|
|
16551
16686
|
throw new ToolNotAllowedError(`\`${exports.AGENT_MEMORY_TOOL}\` is only available to a sub-agent whose definition declares a \`memory:\` scope (project, user or local). Report anything worth remembering in your final message instead \u2014 the main agent decides what to persist.`);
|
|
16552
16687
|
}
|
|
16553
|
-
if (req.tool === "task") {
|
|
16554
|
-
throw new ToolNotAllowedError(
|
|
16688
|
+
if (req.tool === "task" && !canSpawnSubAgents(depth + 1, allowed ?? void 0, depthLimit)) {
|
|
16689
|
+
throw new ToolNotAllowedError(allowed && !allowed.has("task") ? noSpawnReason("denied") : noSpawnReason("depth", depthLimit));
|
|
16555
16690
|
}
|
|
16556
16691
|
if (allowed && !allowed.has(req.tool)) {
|
|
16557
|
-
throw new ToolNotAllowedError(
|
|
16692
|
+
throw new ToolNotAllowedError(
|
|
16693
|
+
// `agent?.name`, NOT `agent!.name`. The non-null assertion held only while `allowed`
|
|
16694
|
+
// was derived solely from `agent?.tools` (non-null allowlist ⇒ named agent). The
|
|
16695
|
+
// parent-intersection broke that invariant: a RESTRICTED parent dispatching `task`
|
|
16696
|
+
// with no subagent_type yields a non-null inherited allowlist with `agent`
|
|
16697
|
+
// undefined, and this line then threw a TypeError instead of ToolNotAllowedError —
|
|
16698
|
+
// which the dispatch site does not recognise, so it laundered the refusal into the
|
|
16699
|
+
// generic "Permission denied by user" this very message exists to avoid.
|
|
16700
|
+
`The "${agent?.name ?? "general-purpose"}" sub-agent is not allowed to use \`${req.tool}\` \u2014 it is not in that agent's tool allowlist. This is a restriction of the agent definition, NOT a user decision: do not ask for approval, use one of the tools you do have, or report back that the task needs a different agent.`
|
|
16701
|
+
);
|
|
16558
16702
|
}
|
|
16559
|
-
if (
|
|
16560
|
-
throw new ToolNotAllowedError(`The "${agent
|
|
16703
|
+
if (testFilesOnly && !allowsTestOnlyWrite(req.tool, req.input)) {
|
|
16704
|
+
throw new ToolNotAllowedError(`The "${agent?.name ?? "general-purpose"}" sub-agent may only write to TEST files, so \`${req.tool}\` was refused for this path. Do not try to work around it: if production code must change, say so in your report instead.`);
|
|
16561
16705
|
}
|
|
16562
16706
|
return options.requestPermission(req);
|
|
16563
16707
|
};
|
|
@@ -16580,6 +16724,8 @@ ${inheritedMd}` : "") : options.nexrallMd;
|
|
|
16580
16724
|
subAbort.aborted = true;
|
|
16581
16725
|
}, 250);
|
|
16582
16726
|
try {
|
|
16727
|
+
if (started)
|
|
16728
|
+
started.value = true;
|
|
16583
16729
|
const result = await runAgentLoop2(subMessages, {
|
|
16584
16730
|
...options,
|
|
16585
16731
|
_depth: depth + 1,
|
|
@@ -16597,15 +16743,21 @@ ${inheritedMd}` : "") : options.nexrallMd;
|
|
|
16597
16743
|
// `tools:` line inherited the parent's allowlist, making the prompt's capability
|
|
16598
16744
|
// claim disagree with its real one.
|
|
16599
16745
|
//
|
|
16600
|
-
//
|
|
16601
|
-
//
|
|
16602
|
-
//
|
|
16603
|
-
//
|
|
16604
|
-
//
|
|
16746
|
+
// This is now LIVE, not latent: nesting is enabled by default, so a grandchild really
|
|
16747
|
+
// can be spawned by an agent that has a memory binding. The explicit `undefined` is
|
|
16748
|
+
// what stops it inheriting that binding and appending to its grandparent's private
|
|
16749
|
+
// notes — a silent cross-agent write rather than a visible error. Note the allowlist
|
|
16750
|
+
// takes the opposite direction on purpose (inherited, because it RESTRICTS); identity
|
|
16751
|
+
// must not be inherited, capability must.
|
|
16605
16752
|
_agentMemory: agent && memoryScope ? { agentName: agent.name, scope: memoryScope } : void 0,
|
|
16606
16753
|
// The same set `gatedPermission` enforces above, so prompt and permission agree
|
|
16607
16754
|
// by construction instead of by two people remembering to update both.
|
|
16608
16755
|
_allowedTools: allowed ?? void 0,
|
|
16756
|
+
// Propagated so a grandchild inherits it too — see _testFilesOnly. Assigned
|
|
16757
|
+
// unconditionally (not by conditional spread) for the same reason as _agentMemory
|
|
16758
|
+
// above: a conditional spread leaves the parent's value in place instead of clearing
|
|
16759
|
+
// it, and here that direction is at least safe, whereas forgetting to propagate is not.
|
|
16760
|
+
_testFilesOnly: testFilesOnly,
|
|
16609
16761
|
editorContext: null,
|
|
16610
16762
|
// fresh isolated context for sub-agent
|
|
16611
16763
|
model: agent?.model ?? options.model,
|
|
@@ -16671,7 +16823,7 @@ ${inheritedMd}` : "") : options.nexrallMd;
|
|
|
16671
16823
|
const mins = Math.round(subtaskTimeoutMs / 6e4);
|
|
16672
16824
|
const partial = capSubTaskText(extractSubTaskText(result, false));
|
|
16673
16825
|
const progress = summariseSubTaskProgress(result);
|
|
16674
|
-
const partialId = (0, agentRegistry_1.rememberAgent)(agent?.name ?? null, typeof input.description === "string" && input.description.trim() || prompt2.slice(0, 80), result);
|
|
16826
|
+
const partialId = (0, agentRegistry_1.rememberAgent)(agent?.name ?? null, typeof input.description === "string" && input.description.trim() || prompt2.slice(0, 80), result, agentScope);
|
|
16675
16827
|
const sections = [
|
|
16676
16828
|
`Sub-task STOPPED after ${mins} minutes with NO PROGRESS (it was not making tool calls or producing output) \u2014 treat everything below as PARTIAL, unverified work, not a finished answer.`,
|
|
16677
16829
|
progress,
|
|
@@ -16683,7 +16835,7 @@ ${partial}` : "",
|
|
|
16683
16835
|
return { error: sections.join("\n\n") };
|
|
16684
16836
|
}
|
|
16685
16837
|
const text = capSubTaskText(extractSubTaskText(result, true));
|
|
16686
|
-
const agentId = resumed ? ((0, agentRegistry_1.updateAgent)(resumed.id, result), resumed.id) : (0, agentRegistry_1.rememberAgent)(agent?.name ?? null, typeof input.description === "string" && input.description.trim() || prompt2.slice(0, 80), result);
|
|
16838
|
+
const agentId = resumed ? ((0, agentRegistry_1.updateAgent)(resumed.id, result, agentScope), resumed.id) : (0, agentRegistry_1.rememberAgent)(agent?.name ?? null, typeof input.description === "string" && input.description.trim() || prompt2.slice(0, 80), result, agentScope);
|
|
16687
16839
|
const body = text || "(sub-task completed with no text output)";
|
|
16688
16840
|
return {
|
|
16689
16841
|
output: `${body}
|
|
@@ -16712,12 +16864,25 @@ ${partial}` : "",
|
|
|
16712
16864
|
}
|
|
16713
16865
|
}
|
|
16714
16866
|
var MODEL_CONTEXT_TOKENS = {
|
|
16867
|
+
// Tier aliases — still sent by older clients, saved settings and sub-agent
|
|
16868
|
+
// frontmatter, so they must keep resolving.
|
|
16715
16869
|
turbo: 1e6,
|
|
16716
16870
|
pro: 1e6,
|
|
16717
|
-
ultra: 1e6
|
|
16871
|
+
ultra: 1e6,
|
|
16872
|
+
fast: 2e5,
|
|
16873
|
+
// Anthropic, by real model id.
|
|
16874
|
+
"claude-sonnet-5": 1e6,
|
|
16875
|
+
"claude-opus-5": 1e6,
|
|
16876
|
+
"claude-fable-5": 1e6,
|
|
16877
|
+
"claude-haiku-4-5-20251001": 2e5,
|
|
16878
|
+
// OpenAI, by real model id (measured).
|
|
16879
|
+
"gpt-5.4": 922e3,
|
|
16880
|
+
"gpt-5.4-mini": 272e3,
|
|
16881
|
+
"gpt-4.1": 1047576,
|
|
16882
|
+
"gpt-4o-mini": 128e3
|
|
16718
16883
|
};
|
|
16719
16884
|
function contextWindowFor2(model) {
|
|
16720
|
-
return MODEL_CONTEXT_TOKENS[model ?? "turbo"] ??
|
|
16885
|
+
return MODEL_CONTEXT_TOKENS[model ?? "turbo"] ?? 128e3;
|
|
16721
16886
|
}
|
|
16722
16887
|
function envFraction(name, fallback) {
|
|
16723
16888
|
const v = Number(process.env[name]);
|
|
@@ -16788,11 +16953,11 @@ ${partial}` : "",
|
|
|
16788
16953
|
for (const m2 of messages) {
|
|
16789
16954
|
for (const b of m2.content) {
|
|
16790
16955
|
if (b.type === "text" && b.text) {
|
|
16791
|
-
parts.push(`${m2.role.toUpperCase()}: ${b.text
|
|
16956
|
+
parts.push(`${m2.role.toUpperCase()}: ${(0, safeSlice_1.sliceSafeEnd)(b.text, 2e3)}`);
|
|
16792
16957
|
} else if (b.type === "tool_use") {
|
|
16793
|
-
parts.push(`${m2.role.toUpperCase()} [tool: ${b.name}]: ${JSON.stringify(b.input ?? {})
|
|
16958
|
+
parts.push(`${m2.role.toUpperCase()} [tool: ${b.name}]: ${(0, safeSlice_1.sliceSafeEnd)(JSON.stringify(b.input ?? {}), 400)}`);
|
|
16794
16959
|
} else if (b.type === "tool_result") {
|
|
16795
|
-
parts.push(`TOOL RESULT: ${String(b.content ?? "")
|
|
16960
|
+
parts.push(`TOOL RESULT: ${(0, safeSlice_1.sliceSafeEnd)(String(b.content ?? ""), 600)}`);
|
|
16796
16961
|
}
|
|
16797
16962
|
}
|
|
16798
16963
|
}
|
|
@@ -16801,8 +16966,8 @@ ${partial}` : "",
|
|
|
16801
16966
|
return full;
|
|
16802
16967
|
const headBudget = Math.floor(MAX_TRANSCRIPT_CHARS * 0.4);
|
|
16803
16968
|
const tailBudget = MAX_TRANSCRIPT_CHARS - headBudget;
|
|
16804
|
-
const head =
|
|
16805
|
-
const tail =
|
|
16969
|
+
const head = (0, safeSlice_1.sliceSafeEnd)(full, headBudget);
|
|
16970
|
+
const tail = (0, safeSlice_1.sliceSafeStart)(full, full.length - tailBudget);
|
|
16806
16971
|
const dropped = full.length - head.length - tail.length;
|
|
16807
16972
|
return `${head}
|
|
16808
16973
|
|
|
@@ -17087,7 +17252,8 @@ Continue the work from here.` }] });
|
|
|
17087
17252
|
const agentScope = options._agentScope ?? "root";
|
|
17088
17253
|
const settings = (0, rules_1.loadSettings)(options.workDir);
|
|
17089
17254
|
const agentTypes = (0, agentTypes_1.loadAgentTypes)(options.workDir).filter((t2) => (0, rules_1.evaluatePermission)(settings.permissions, "task", { subagent_type: t2.name }, options.workDir) !== "deny");
|
|
17090
|
-
const
|
|
17255
|
+
const depthLimit = resolveMaxSubagentDepth(settings.raw);
|
|
17256
|
+
const maySpawn = canSpawnSubAgents(depth, options._allowedTools, depthLimit);
|
|
17091
17257
|
const agentsCatalogue = maySpawn ? (0, agentTypes_1.summariseAgents)(agentTypes) : "";
|
|
17092
17258
|
const skillsCatalogue = (0, skills_1.summariseSkills)((0, skills_1.loadSkills)(options.workDir));
|
|
17093
17259
|
const planAwareNexrallMd = options.planMode ? planMode_1.PLAN_MODE_INSTRUCTIONS + (options.nexrallMd ? `
|
|
@@ -17384,19 +17550,29 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
17384
17550
|
if (!permitted) {
|
|
17385
17551
|
result = { error: deniedReason ?? "Permission denied by user" };
|
|
17386
17552
|
} else if (name === "task") {
|
|
17387
|
-
|
|
17388
|
-
|
|
17389
|
-
|
|
17390
|
-
|
|
17553
|
+
const overBudget = claimSessionSubAgentSlot(options.workDir, options.onNotice ?? options.onText);
|
|
17554
|
+
if (overBudget) {
|
|
17555
|
+
result = { error: overBudget };
|
|
17556
|
+
} else {
|
|
17557
|
+
const childDepth = depth + 1;
|
|
17558
|
+
const { run: limitRun, max: limitMax } = subTaskLimiter(childDepth, options.workDir);
|
|
17559
|
+
const inFlight = _inFlightByDepth.get(childDepth) ?? 0;
|
|
17560
|
+
if (inFlight >= limitMax) {
|
|
17561
|
+
(options.onNotice ?? options.onText)(`\u23F3 Queued: ${limitMax} sub-agent(s) already running at this level, so this one starts when a slot frees up (raise "maxConcurrentSubtasks" in .nexrall/settings.json to widen it).`);
|
|
17391
17562
|
}
|
|
17392
|
-
|
|
17563
|
+
_inFlightByDepth.set(childDepth, inFlight + 1);
|
|
17564
|
+
const started = { value: false };
|
|
17393
17565
|
try {
|
|
17394
|
-
result = await limitRun(() => runSubTask(input, options, agentTypes));
|
|
17566
|
+
result = await limitRun(() => runSubTask(input, options, agentTypes, started));
|
|
17395
17567
|
} finally {
|
|
17396
|
-
|
|
17568
|
+
if (!started.value)
|
|
17569
|
+
refundSessionSubAgentSlot();
|
|
17570
|
+
const n = (_inFlightByDepth.get(childDepth) ?? 1) - 1;
|
|
17571
|
+
if (n > 0)
|
|
17572
|
+
_inFlightByDepth.set(childDepth, n);
|
|
17573
|
+
else
|
|
17574
|
+
_inFlightByDepth.delete(childDepth);
|
|
17397
17575
|
}
|
|
17398
|
-
} else {
|
|
17399
|
-
result = await runSubTask(input, options, agentTypes);
|
|
17400
17576
|
}
|
|
17401
17577
|
} else {
|
|
17402
17578
|
const pre = runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir);
|
|
@@ -64372,7 +64548,7 @@ function TrustPrompt({ workDir, signals, onDecide }) {
|
|
|
64372
64548
|
decide(false);
|
|
64373
64549
|
return;
|
|
64374
64550
|
}
|
|
64375
|
-
if (key.return ||
|
|
64551
|
+
if (key.return || /[\r\n]/.test(input)) {
|
|
64376
64552
|
decide(indexRef.current === 0);
|
|
64377
64553
|
}
|
|
64378
64554
|
});
|
|
@@ -64575,14 +64751,41 @@ var InkReadlineAdapter = class extends EventEmitter3 {
|
|
|
64575
64751
|
};
|
|
64576
64752
|
|
|
64577
64753
|
// src/commands/chat.ts
|
|
64578
|
-
var CLI_VERSION = "0.5.
|
|
64754
|
+
var CLI_VERSION = "0.5.61";
|
|
64579
64755
|
var MODEL_LABELS = {
|
|
64580
|
-
|
|
64581
|
-
|
|
64582
|
-
|
|
64756
|
+
"claude-sonnet-5": "Claude Sonnet 5",
|
|
64757
|
+
"claude-opus-5": "Claude Opus 5",
|
|
64758
|
+
"claude-fable-5": "Claude Fable 5",
|
|
64759
|
+
"claude-haiku-4-5-20251001": "Claude Haiku 4.5",
|
|
64760
|
+
"gpt-5.4": "GPT-5.4",
|
|
64761
|
+
"gpt-5.4-mini": "GPT-5.4 Mini",
|
|
64762
|
+
"gpt-4.1": "GPT-4.1"
|
|
64763
|
+
};
|
|
64764
|
+
var LEGACY_MODEL_ALIASES = {
|
|
64765
|
+
turbo: "claude-sonnet-5",
|
|
64766
|
+
auto: "claude-sonnet-5",
|
|
64767
|
+
pro: "claude-opus-5",
|
|
64768
|
+
power: "claude-opus-5",
|
|
64769
|
+
ultra: "claude-fable-5",
|
|
64770
|
+
fast: "claude-haiku-4-5-20251001"
|
|
64583
64771
|
};
|
|
64772
|
+
var SELECTABLE_MODELS = [
|
|
64773
|
+
"claude-sonnet-5",
|
|
64774
|
+
"claude-opus-5",
|
|
64775
|
+
"claude-fable-5",
|
|
64776
|
+
"gpt-5.4",
|
|
64777
|
+
"gpt-5.4-mini",
|
|
64778
|
+
"gpt-4.1"
|
|
64779
|
+
];
|
|
64780
|
+
function normaliseModelId(model) {
|
|
64781
|
+
const raw = (model ?? "").trim();
|
|
64782
|
+
if (!raw)
|
|
64783
|
+
return "claude-sonnet-5";
|
|
64784
|
+
return LEGACY_MODEL_ALIASES[raw.toLowerCase()] ?? raw;
|
|
64785
|
+
}
|
|
64584
64786
|
function resolveModelLabel(alias) {
|
|
64585
|
-
|
|
64787
|
+
const id = normaliseModelId(alias);
|
|
64788
|
+
return MODEL_LABELS[id] ?? id;
|
|
64586
64789
|
}
|
|
64587
64790
|
function tryExec(cmd, cwd2) {
|
|
64588
64791
|
try {
|
|
@@ -65097,7 +65300,7 @@ function printHelp() {
|
|
|
65097
65300
|
["/init", "Generate nexrall.md for this project"],
|
|
65098
65301
|
["/memory [global] [clear]", "View persistent memory (project or global); /memory clear to wipe"],
|
|
65099
65302
|
["/help", "Show this help"],
|
|
65100
|
-
["/model [
|
|
65303
|
+
["/model [name]", "Switch model (e.g. claude-opus-5, gpt-5.4)"],
|
|
65101
65304
|
["/mode [ask|edit|plan|auto]", "Set agent mode"],
|
|
65102
65305
|
["/effort [low|medium|high|extra]", "Set thinking effort level"],
|
|
65103
65306
|
["/yolo", "Auto-approve all permissions"],
|
|
@@ -65158,6 +65361,9 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
|
|
|
65158
65361
|
process.stdout.write("Not trusted \u2014 exiting without loading this folder.\n");
|
|
65159
65362
|
process.exit(0);
|
|
65160
65363
|
}
|
|
65364
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
65365
|
+
if (process.stdin.isTTY)
|
|
65366
|
+
process.stdin.setRawMode(true);
|
|
65161
65367
|
}
|
|
65162
65368
|
const env3 = collectEnv(workDir);
|
|
65163
65369
|
const nexrallMd = readNexrallMd(workDir);
|
|
@@ -65387,6 +65593,7 @@ ${text}` : "");
|
|
|
65387
65593
|
messages = [];
|
|
65388
65594
|
sessionId = crypto.randomBytes(8).toString("hex");
|
|
65389
65595
|
sessionTitle = "";
|
|
65596
|
+
(0, import_code_core4.resetSessionSubAgentBudget)();
|
|
65390
65597
|
console.log(source_default.dim(" Conversation cleared. New session started."));
|
|
65391
65598
|
rl.prompt();
|
|
65392
65599
|
return;
|
|
@@ -65623,14 +65830,15 @@ ${dirList}`;
|
|
|
65623
65830
|
rl.prompt();
|
|
65624
65831
|
return;
|
|
65625
65832
|
case "/model": {
|
|
65626
|
-
const
|
|
65627
|
-
if (!
|
|
65628
|
-
console.log(source_default.dim(` Current: ${source_default.cyan(resolveModelLabel(modelAlias))}
|
|
65629
|
-
|
|
65630
|
-
|
|
65631
|
-
|
|
65833
|
+
const requested = arg.trim();
|
|
65834
|
+
if (!requested) {
|
|
65835
|
+
console.log(source_default.dim(` Current: ${source_default.cyan(resolveModelLabel(modelAlias))}`));
|
|
65836
|
+
console.log(source_default.dim(` Options: ${SELECTABLE_MODELS.join(" \xB7 ")}`));
|
|
65837
|
+
} else if (SELECTABLE_MODELS.includes(normaliseModelId(requested))) {
|
|
65838
|
+
modelAlias = normaliseModelId(requested);
|
|
65839
|
+
console.log(source_default.green(` Model \u2192 ${source_default.bold(resolveModelLabel(modelAlias))}`));
|
|
65632
65840
|
} else {
|
|
65633
|
-
console.log(source_default.red(` Unknown: ${
|
|
65841
|
+
console.log(source_default.red(` Unknown: ${requested}. Options: ${SELECTABLE_MODELS.join(", ")}`));
|
|
65634
65842
|
}
|
|
65635
65843
|
rl.prompt();
|
|
65636
65844
|
return;
|
|
@@ -66246,7 +66454,7 @@ function pluginSourceRemoveCommand(name, opts) {
|
|
|
66246
66454
|
|
|
66247
66455
|
// src/index.ts
|
|
66248
66456
|
var program2 = new Command();
|
|
66249
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
66457
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.61").enablePositionalOptions();
|
|
66250
66458
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
66251
66459
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
66252
66460
|
program2.command("update").description("Update nex to the latest version").option("-c, --check", "Check for updates without installing").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
|
|
@@ -66317,12 +66525,12 @@ program2.command("sessions").description("List or delete saved chat sessions").o
|
|
|
66317
66525
|
console.log(source_default.dim(" nex sessions --delete <id> to remove one"));
|
|
66318
66526
|
console.log();
|
|
66319
66527
|
});
|
|
66320
|
-
program2.option("-p, --pro", "Use
|
|
66528
|
+
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("--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)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
|
|
66321
66529
|
if (!(0, import_code_core6.isAuthenticated)()) {
|
|
66322
66530
|
console.error("Not logged in. Run: nex auth");
|
|
66323
66531
|
process.exit(1);
|
|
66324
66532
|
}
|
|
66325
|
-
const model = options.model ? options.model : options.ultra ? "
|
|
66533
|
+
const model = options.model ? options.model : options.ultra ? "claude-fable-5" : options.pro ? "claude-opus-5" : options.turbo ? "claude-sonnet-5" : "claude-sonnet-5";
|
|
66326
66534
|
const workDir = options.dir ? path5.resolve(options.dir) : process.cwd();
|
|
66327
66535
|
if (options.yolo)
|
|
66328
66536
|
setAutoApprove("all");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexrall-code",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.61",
|
|
4
4
|
"description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -31,7 +31,15 @@
|
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=18"
|
|
33
33
|
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "node build.js",
|
|
36
|
+
"dev": "tsx src/index.ts",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"test": "tsc --noEmit && tsc --noEmit -p test/tsconfig.json && node --import tsx --test test/*.test.ts test/*.test.mts",
|
|
39
|
+
"release": "node build.js && node scripts/upload-release.cjs"
|
|
40
|
+
},
|
|
34
41
|
"dependencies": {
|
|
42
|
+
"@nexrall/code-core": "^1.4.35",
|
|
35
43
|
"chalk": "^5.3.0",
|
|
36
44
|
"commander": "^12.0.0",
|
|
37
45
|
"diff": "^5.2.0",
|
|
@@ -40,8 +48,7 @@
|
|
|
40
48
|
"prompts": "^2.4.2",
|
|
41
49
|
"react": "^19.2.8",
|
|
42
50
|
"readline": "^1.3.0",
|
|
43
|
-
"string-width": "^7.2.0"
|
|
44
|
-
"@nexrall/code-core": "1.4.33"
|
|
51
|
+
"string-width": "^7.2.0"
|
|
45
52
|
},
|
|
46
53
|
"devDependencies": {
|
|
47
54
|
"@aws-sdk/client-s3": "^3.600.0",
|
|
@@ -52,12 +59,5 @@
|
|
|
52
59
|
"esbuild": "^0.20.0",
|
|
53
60
|
"tsx": "^4.7.0",
|
|
54
61
|
"typescript": "^6.0.3"
|
|
55
|
-
},
|
|
56
|
-
"scripts": {
|
|
57
|
-
"build": "node build.js",
|
|
58
|
-
"dev": "tsx src/index.ts",
|
|
59
|
-
"start": "node dist/index.js",
|
|
60
|
-
"test": "tsc --noEmit && tsc --noEmit -p test/tsconfig.json && node --import tsx --test test/*.test.ts test/*.test.mts",
|
|
61
|
-
"release": "node build.js && node scripts/upload-release.cjs"
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|