nexrall-code 0.5.54 → 0.5.55
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 +157 -22
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -14718,6 +14718,29 @@ var require_agentTypes = __commonJS({
|
|
|
14718
14718
|
"notebook_edit"
|
|
14719
14719
|
];
|
|
14720
14720
|
var BUILTIN_AGENTS = [
|
|
14721
|
+
{
|
|
14722
|
+
// The catch-all, matching Claude Code's `general-purpose`.
|
|
14723
|
+
//
|
|
14724
|
+
// This capability already existed — omitting `subagent_type` gives an unrestricted
|
|
14725
|
+
// sub-agent — but it had no NAME, and that had two consequences worth fixing:
|
|
14726
|
+
//
|
|
14727
|
+
// 1. `permissions.deny: ["task(...)"]` matches on the agent name, so the ONE
|
|
14728
|
+
// sub-agent that can write files and run bash was the one variant a project
|
|
14729
|
+
// could not disable individually. Only a blanket `deny: ["task"]` reached it.
|
|
14730
|
+
// 2. The model had to infer that leaving the field blank was even an option, so
|
|
14731
|
+
// it would sometimes pick a specialist that fitted badly (an unrestricted
|
|
14732
|
+
// explorer) rather than the general worker it actually wanted.
|
|
14733
|
+
//
|
|
14734
|
+
// `tools` is deliberately UNDEFINED, which means "no allowlist" — full access,
|
|
14735
|
+
// 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. The safety
|
|
14737
|
+
// properties elsewhere still apply: plan mode is inherited, the user's permission
|
|
14738
|
+
// gate still runs on every call, and it cannot spawn further sub-agents.
|
|
14739
|
+
name: "general-purpose",
|
|
14740
|
+
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
|
+
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.",
|
|
14742
|
+
source: "builtin"
|
|
14743
|
+
},
|
|
14721
14744
|
{
|
|
14722
14745
|
name: "reviewer",
|
|
14723
14746
|
description: "Read-only code reviewer \u2014 finds correctness bugs, edge cases, and security issues in a diff or file set. Cannot modify files.",
|
|
@@ -14780,6 +14803,9 @@ var require_agentTypes = __commonJS({
|
|
|
14780
14803
|
// frontier model, and this is the agent most likely to be spawned in bulk.
|
|
14781
14804
|
{
|
|
14782
14805
|
name: "explorer",
|
|
14806
|
+
// Lean prompt: this agent exists to keep bulk searching cheap, and it reports
|
|
14807
|
+
// findings for the MAIN agent to interpret with full project context.
|
|
14808
|
+
lightPrompt: true,
|
|
14783
14809
|
description: "Fast read-only codebase explorer \u2014 locates files, symbols, and call sites and reports concise findings. Use to keep bulk searching out of the main context. Cannot modify files.",
|
|
14784
14810
|
tools: READ_ONLY_TOOLS,
|
|
14785
14811
|
model: "turbo",
|
|
@@ -15877,9 +15903,11 @@ var require_loop = __commonJS({
|
|
|
15877
15903
|
exports.stopReasonNotice = stopReasonNotice;
|
|
15878
15904
|
exports.resolveMaxIterations = resolveMaxIterations;
|
|
15879
15905
|
exports.createLimiter = createLimiter;
|
|
15906
|
+
exports.resolveSubtaskTimeoutMs = resolveSubtaskTimeoutMs;
|
|
15880
15907
|
exports.extractSubTaskText = extractSubTaskText;
|
|
15881
15908
|
exports.capSubTaskText = capSubTaskText;
|
|
15882
15909
|
exports.summariseSubTaskProgress = summariseSubTaskProgress;
|
|
15910
|
+
exports.lastToolResults = lastToolResults;
|
|
15883
15911
|
exports.contextWindowFor = contextWindowFor2;
|
|
15884
15912
|
exports.compactionThresholds = compactionThresholds2;
|
|
15885
15913
|
exports.estimateBodyBytes = estimateBodyBytes2;
|
|
@@ -16222,7 +16250,17 @@ ${ctx.repeatError}
|
|
|
16222
16250
|
}
|
|
16223
16251
|
var MAX_TASK_DEPTH = 1;
|
|
16224
16252
|
var _subTaskCounter = 0;
|
|
16225
|
-
var
|
|
16253
|
+
var DEFAULT_SUBTASK_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
16254
|
+
function resolveSubtaskTimeoutMs(settingsRaw) {
|
|
16255
|
+
const fromEnv = Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS);
|
|
16256
|
+
if (Number.isFinite(fromEnv) && fromEnv > 0)
|
|
16257
|
+
return Math.floor(fromEnv);
|
|
16258
|
+
const raw = settingsRaw.subtaskTimeoutMs;
|
|
16259
|
+
const fromSettings = Number(raw);
|
|
16260
|
+
if (Number.isFinite(fromSettings) && fromSettings > 0)
|
|
16261
|
+
return Math.floor(fromSettings);
|
|
16262
|
+
return DEFAULT_SUBTASK_TIMEOUT_MS;
|
|
16263
|
+
}
|
|
16226
16264
|
var SUBTASK_MAX = 48e3;
|
|
16227
16265
|
var ToolNotAllowedError = class extends Error {
|
|
16228
16266
|
constructor(message) {
|
|
@@ -16279,11 +16317,59 @@ ${tail}`;
|
|
|
16279
16317
|
}
|
|
16280
16318
|
if (toolNames.length === 0)
|
|
16281
16319
|
return "";
|
|
16320
|
+
const recentFindings = lastToolResults(messages, SALVAGE_RESULT_COUNT, SALVAGE_RESULT_CHARS);
|
|
16282
16321
|
const counts = /* @__PURE__ */ new Map();
|
|
16283
16322
|
for (const n of toolNames)
|
|
16284
16323
|
counts.set(n, (counts.get(n) ?? 0) + 1);
|
|
16285
16324
|
const inventory = [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([name, n]) => n > 1 ? `${name} \xD7${n}` : name).join(", ");
|
|
16286
|
-
|
|
16325
|
+
const header = `Tool calls completed before it was stopped (${toolNames.length} total): ${inventory}.`;
|
|
16326
|
+
return recentFindings ? `${header}
|
|
16327
|
+
|
|
16328
|
+
What its most recent tool calls actually returned (use this instead of repeating them):
|
|
16329
|
+
${recentFindings}` : header;
|
|
16330
|
+
}
|
|
16331
|
+
var SALVAGE_RESULT_COUNT = 4;
|
|
16332
|
+
var SALVAGE_RESULT_CHARS = 2e3;
|
|
16333
|
+
function lastToolResults(messages, count, maxChars) {
|
|
16334
|
+
const nameById = /* @__PURE__ */ new Map();
|
|
16335
|
+
for (const m2 of messages) {
|
|
16336
|
+
if (m2.role !== "assistant" || !Array.isArray(m2.content))
|
|
16337
|
+
continue;
|
|
16338
|
+
for (const b of m2.content) {
|
|
16339
|
+
if (b?.type === "tool_use" && b.id && typeof b.name === "string")
|
|
16340
|
+
nameById.set(b.id, b.name);
|
|
16341
|
+
}
|
|
16342
|
+
}
|
|
16343
|
+
const out = [];
|
|
16344
|
+
for (let i2 = messages.length - 1; i2 >= 0 && out.length < count; i2--) {
|
|
16345
|
+
const m2 = messages[i2];
|
|
16346
|
+
if (m2.role !== "user" || !Array.isArray(m2.content))
|
|
16347
|
+
continue;
|
|
16348
|
+
for (const b of [...m2.content].reverse()) {
|
|
16349
|
+
if (out.length >= count)
|
|
16350
|
+
break;
|
|
16351
|
+
if (b?.type !== "tool_result")
|
|
16352
|
+
continue;
|
|
16353
|
+
const text = toolResultText(b);
|
|
16354
|
+
if (!text)
|
|
16355
|
+
continue;
|
|
16356
|
+
const name = nameById.get(String(b.tool_use_id ?? "")) ?? "tool";
|
|
16357
|
+
const body = text.length > maxChars ? `${sliceSafeEnd(text, maxChars)}
|
|
16358
|
+
\u2026 [truncated]` : text;
|
|
16359
|
+
out.push(`\u2022 ${name}:
|
|
16360
|
+
${body}`);
|
|
16361
|
+
}
|
|
16362
|
+
}
|
|
16363
|
+
return out.reverse().join("\n\n");
|
|
16364
|
+
}
|
|
16365
|
+
function toolResultText(block) {
|
|
16366
|
+
const c = block.content;
|
|
16367
|
+
if (typeof c === "string")
|
|
16368
|
+
return c.trim();
|
|
16369
|
+
if (Array.isArray(c)) {
|
|
16370
|
+
return c.filter((x2) => x2?.type === "text" && typeof x2.text === "string").map((x2) => x2.text).join("\n").trim();
|
|
16371
|
+
}
|
|
16372
|
+
return "";
|
|
16287
16373
|
}
|
|
16288
16374
|
async function runSubTask(input, options, agentTypes) {
|
|
16289
16375
|
const prompt2 = typeof input.prompt === "string" ? input.prompt.trim() : "";
|
|
@@ -16324,16 +16410,17 @@ If you just created .nexrall/agents/` + requestedType + ".md, make sure the writ
|
|
|
16324
16410
|
}
|
|
16325
16411
|
const memoryScope = agent?.memory;
|
|
16326
16412
|
const agentMemoryNotes = agent && memoryScope ? (0, memory_1.agentMemoryPreamble)(agent.name, memoryScope, options.workDir) : "";
|
|
16413
|
+
const inheritedMd = agent?.lightPrompt ? "" : options.nexrallMd ?? "";
|
|
16327
16414
|
const subNexrallMd = agent ? `# Sub-agent role: ${agent.name}
|
|
16328
16415
|
${agent.prompt}` + (agentMemoryNotes ? `
|
|
16329
16416
|
|
|
16330
16417
|
---
|
|
16331
16418
|
|
|
16332
|
-
${agentMemoryNotes}` : "") + (
|
|
16419
|
+
${agentMemoryNotes}` : "") + (inheritedMd ? `
|
|
16333
16420
|
|
|
16334
16421
|
---
|
|
16335
16422
|
|
|
16336
|
-
${
|
|
16423
|
+
${inheritedMd}` : "") : options.nexrallMd;
|
|
16337
16424
|
const allowed = agent?.tools ? new Set(agent.tools) : null;
|
|
16338
16425
|
if (allowed && memoryScope)
|
|
16339
16426
|
allowed.add(exports.AGENT_MEMORY_TOOL);
|
|
@@ -16351,9 +16438,18 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16351
16438
|
};
|
|
16352
16439
|
const subMessages = resumed ? [...resumed.messages, { role: "user", content: [{ type: "text", text: prompt2 }] }] : [{ role: "user", content: [{ type: "text", text: prompt2 }] }];
|
|
16353
16440
|
const subAbort = { aborted: false };
|
|
16354
|
-
const
|
|
16355
|
-
|
|
16356
|
-
|
|
16441
|
+
const subtaskTimeoutMs = resolveSubtaskTimeoutMs((0, rules_1.loadSettings)(options.workDir).raw);
|
|
16442
|
+
let lastProgressAt = Date.now();
|
|
16443
|
+
let stalled = false;
|
|
16444
|
+
const bumpProgress = () => {
|
|
16445
|
+
lastProgressAt = Date.now();
|
|
16446
|
+
};
|
|
16447
|
+
const stallWatchdog = setInterval(() => {
|
|
16448
|
+
if (Date.now() - lastProgressAt > subtaskTimeoutMs) {
|
|
16449
|
+
stalled = true;
|
|
16450
|
+
subAbort.aborted = true;
|
|
16451
|
+
}
|
|
16452
|
+
}, 1e3);
|
|
16357
16453
|
const parentAbortPoll = setInterval(() => {
|
|
16358
16454
|
if (options.abortSignal?.aborted)
|
|
16359
16455
|
subAbort.aborted = true;
|
|
@@ -16364,11 +16460,27 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16364
16460
|
_depth: depth + 1,
|
|
16365
16461
|
_agentScope: `sub_${++_subTaskCounter}`,
|
|
16366
16462
|
// isolated todo store per sub-agent
|
|
16367
|
-
//
|
|
16368
|
-
|
|
16463
|
+
// ── Assigned UNCONDITIONALLY, never by conditional spread ────────────────
|
|
16464
|
+
//
|
|
16465
|
+
// These two were previously spread in only when set:
|
|
16466
|
+
//
|
|
16467
|
+
// ...(agent && memoryScope ? { _agentMemory: … } : {}),
|
|
16468
|
+
//
|
|
16469
|
+
// which does NOT clear the key — it leaves whatever `...options` already had.
|
|
16470
|
+
// So a child WITHOUT its own `memory:` inherited its PARENT's binding and would
|
|
16471
|
+
// have appended to another agent's private notes; likewise an agent with no
|
|
16472
|
+
// `tools:` line inherited the parent's allowlist, making the prompt's capability
|
|
16473
|
+
// claim disagree with its real one.
|
|
16474
|
+
//
|
|
16475
|
+
// MAX_TASK_DEPTH === 1 means no nested spawn can reach this today, so it is
|
|
16476
|
+
// latent rather than live — but the limiter comment below explicitly contemplates
|
|
16477
|
+
// raising that depth, and this is exactly the kind of leak that would come back
|
|
16478
|
+
// as a security bug rather than a visible error. Explicit undefined makes the
|
|
16479
|
+
// child's identity independent of the parent's by construction.
|
|
16480
|
+
_agentMemory: agent && memoryScope ? { agentName: agent.name, scope: memoryScope } : void 0,
|
|
16369
16481
|
// The same set `gatedPermission` enforces above, so prompt and permission agree
|
|
16370
16482
|
// by construction instead of by two people remembering to update both.
|
|
16371
|
-
|
|
16483
|
+
_allowedTools: allowed ?? void 0,
|
|
16372
16484
|
editorContext: null,
|
|
16373
16485
|
// fresh isolated context for sub-agent
|
|
16374
16486
|
model: agent?.model ?? options.model,
|
|
@@ -16400,25 +16512,48 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16400
16512
|
// Forward tool events with isSubTask=true so the UI can render a badge
|
|
16401
16513
|
// instead of prepending "[sub-task]" to the tool name (which caused double-prefix
|
|
16402
16514
|
// when the name was already labelled, and mixed display concerns into the data layer).
|
|
16403
|
-
|
|
16404
|
-
|
|
16515
|
+
// Every tool event is PROGRESS: it proves the sub-agent is still doing work, which
|
|
16516
|
+
// is what the stall watchdog above measures. Bumping on both use and result means a
|
|
16517
|
+
// single very slow tool (a long test run) resets the clock when it starts AND when
|
|
16518
|
+
// it finishes, so it cannot be mistaken for a hang.
|
|
16519
|
+
onToolUse: (n, i2) => {
|
|
16520
|
+
bumpProgress();
|
|
16521
|
+
options.onToolUse(n, i2, true);
|
|
16522
|
+
},
|
|
16523
|
+
onToolResult: (n, r2) => {
|
|
16524
|
+
bumpProgress();
|
|
16525
|
+
options.onToolResult(n, r2, true);
|
|
16526
|
+
},
|
|
16405
16527
|
onToolStreamChunk: (n, c) => options.onToolStreamChunk?.(n, c, true),
|
|
16406
16528
|
// Forward thinking so the UI shows the indicator while sub-agent reasons
|
|
16407
|
-
|
|
16408
|
-
|
|
16409
|
-
|
|
16529
|
+
// Thinking is progress too — a model reasoning for minutes on a hard problem is
|
|
16530
|
+
// working, not stalled. Without this, deep reasoning on an expensive tier would
|
|
16531
|
+
// trip the watchdog precisely when the sub-agent was most valuable.
|
|
16532
|
+
onThinking: (text2) => {
|
|
16533
|
+
bumpProgress();
|
|
16534
|
+
options.onThinking?.(text2);
|
|
16535
|
+
},
|
|
16536
|
+
onThinkingDelta: (text2) => {
|
|
16537
|
+
bumpProgress();
|
|
16538
|
+
options.onThinkingDelta?.(text2);
|
|
16539
|
+
},
|
|
16540
|
+
onThinkingProgress: (tok) => {
|
|
16541
|
+
bumpProgress();
|
|
16542
|
+
options.onThinkingProgress?.(tok);
|
|
16543
|
+
}
|
|
16410
16544
|
});
|
|
16411
|
-
if (
|
|
16412
|
-
const mins = Math.round(
|
|
16545
|
+
if (stalled && !options.abortSignal?.aborted) {
|
|
16546
|
+
const mins = Math.round(subtaskTimeoutMs / 6e4);
|
|
16413
16547
|
const partial = capSubTaskText(extractSubTaskText(result, false));
|
|
16414
16548
|
const progress = summariseSubTaskProgress(result);
|
|
16549
|
+
const partialId = (0, agentRegistry_1.rememberAgent)(agent?.name ?? null, typeof input.description === "string" && input.description.trim() || prompt2.slice(0, 80), result);
|
|
16415
16550
|
const sections = [
|
|
16416
|
-
`Sub-task STOPPED after ${mins} minutes
|
|
16551
|
+
`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.`,
|
|
16417
16552
|
progress,
|
|
16418
16553
|
partial ? `Partial output before it was stopped:
|
|
16419
16554
|
|
|
16420
16555
|
${partial}` : "",
|
|
16421
|
-
|
|
16556
|
+
`Do NOT re-run the same sub-task from scratch. Either build on what is above, or continue THIS run with resume_agent_id="${partialId}" (it still has everything it read), or split the remaining work into smaller, more focused sub-tasks.`
|
|
16422
16557
|
].filter(Boolean);
|
|
16423
16558
|
return { error: sections.join("\n\n") };
|
|
16424
16559
|
}
|
|
@@ -16447,7 +16582,7 @@ ${partial}` : "",
|
|
|
16447
16582
|
}
|
|
16448
16583
|
return { error: `Sub-task failed: ${err.message}` };
|
|
16449
16584
|
} finally {
|
|
16450
|
-
|
|
16585
|
+
clearInterval(stallWatchdog);
|
|
16451
16586
|
clearInterval(parentAbortPoll);
|
|
16452
16587
|
}
|
|
16453
16588
|
}
|
|
@@ -64214,7 +64349,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
|
|
|
64214
64349
|
};
|
|
64215
64350
|
|
|
64216
64351
|
// src/commands/chat.ts
|
|
64217
|
-
var CLI_VERSION = "0.5.
|
|
64352
|
+
var CLI_VERSION = "0.5.55";
|
|
64218
64353
|
var MODEL_LABELS = {
|
|
64219
64354
|
turbo: "Nexrall Turbo",
|
|
64220
64355
|
pro: "Nexrall Pro",
|
|
@@ -65881,7 +66016,7 @@ function pluginSourceRemoveCommand(name, opts) {
|
|
|
65881
66016
|
|
|
65882
66017
|
// src/index.ts
|
|
65883
66018
|
var program2 = new Command();
|
|
65884
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
66019
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.55").enablePositionalOptions();
|
|
65885
66020
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
65886
66021
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
65887
66022
|
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) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexrall-code",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.55",
|
|
4
4
|
"description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"react": "^19.2.8",
|
|
42
42
|
"readline": "^1.3.0",
|
|
43
43
|
"string-width": "^7.2.0",
|
|
44
|
-
"@nexrall/code-core": "1.4.
|
|
44
|
+
"@nexrall/code-core": "1.4.30"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@aws-sdk/client-s3": "^3.600.0",
|