nexrall-code 0.5.52 → 0.5.54
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 +244 -34
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9982,7 +9982,7 @@ var require_client = __commonJS({
|
|
|
9982
9982
|
}
|
|
9983
9983
|
var MAX_TOTAL_ATTEMPTS = (MAX_RETRIES + 1) * (MAX_RETRIES + 1);
|
|
9984
9984
|
async function streamChat(messages, options, onEvent) {
|
|
9985
|
-
const { model, env: env3, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender } = options;
|
|
9985
|
+
const { model, env: env3, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender, canWriteSharedMemory, hasOwnAgentStore } = options;
|
|
9986
9986
|
let turnId = (0, crypto_1.randomUUID)();
|
|
9987
9987
|
const controller = new AbortController();
|
|
9988
9988
|
if (abortSignal?.aborted)
|
|
@@ -10023,7 +10023,7 @@ var require_client = __commonJS({
|
|
|
10023
10023
|
{
|
|
10024
10024
|
method: "POST",
|
|
10025
10025
|
headers: authHeaders(),
|
|
10026
|
-
body: JSON.stringify({ messages, model, env: env3, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId }),
|
|
10026
|
+
body: JSON.stringify({ messages, model, env: env3, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore }),
|
|
10027
10027
|
signal: controller.signal
|
|
10028
10028
|
}
|
|
10029
10029
|
];
|
|
@@ -11012,13 +11012,18 @@ var require_memory = __commonJS({
|
|
|
11012
11012
|
};
|
|
11013
11013
|
}();
|
|
11014
11014
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11015
|
-
exports.MEMORY_HARD_CAP_BYTES = exports.MEMORY_COMPACT_TRIGGER_BYTES = exports.MEMORY_MAX_BYTES = exports.MEMORY_ENTRY_MAX_CHARS = void 0;
|
|
11015
|
+
exports.AGENT_MEMORY_MAX_BYTES = exports.AGENT_MEMORY_INJECT_MAX = exports.MEMORY_HARD_CAP_BYTES = exports.MEMORY_COMPACT_TRIGGER_BYTES = exports.MEMORY_MAX_BYTES = exports.MEMORY_ENTRY_MAX_CHARS = void 0;
|
|
11016
11016
|
exports.memoryFilePath = memoryFilePath;
|
|
11017
11017
|
exports.writeMemory = writeMemory;
|
|
11018
11018
|
exports.readMemory = readMemory2;
|
|
11019
11019
|
exports.readAllMemory = readAllMemory2;
|
|
11020
11020
|
exports.clearMemory = clearMemory2;
|
|
11021
11021
|
exports.memoryStats = memoryStats2;
|
|
11022
|
+
exports.isSafeAgentName = isSafeAgentName;
|
|
11023
|
+
exports.agentMemoryPath = agentMemoryPath;
|
|
11024
|
+
exports.readAgentMemory = readAgentMemory;
|
|
11025
|
+
exports.writeAgentMemory = writeAgentMemory;
|
|
11026
|
+
exports.agentMemoryPreamble = agentMemoryPreamble;
|
|
11022
11027
|
exports.compactMemoryIfNeeded = compactMemoryIfNeeded2;
|
|
11023
11028
|
var fs9 = __importStar(__require("fs"));
|
|
11024
11029
|
var os6 = __importStar(__require("os"));
|
|
@@ -11127,6 +11132,71 @@ ${projMem}`);
|
|
|
11127
11132
|
const entries = content.split("\n").filter((l) => /^-\s\[\d{4}-\d{2}-\d{2}\]/.test(l)).length;
|
|
11128
11133
|
return { file, bytes: Buffer.byteLength(content, "utf-8"), entries };
|
|
11129
11134
|
}
|
|
11135
|
+
exports.AGENT_MEMORY_INJECT_MAX = 8e3;
|
|
11136
|
+
exports.AGENT_MEMORY_MAX_BYTES = 24e3;
|
|
11137
|
+
function isSafeAgentName(name) {
|
|
11138
|
+
return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) && !name.includes("..");
|
|
11139
|
+
}
|
|
11140
|
+
function agentMemoryPath(agentName, scope, workDir) {
|
|
11141
|
+
if (!isSafeAgentName(agentName))
|
|
11142
|
+
return null;
|
|
11143
|
+
const file = `${agentName}.md`;
|
|
11144
|
+
let dir;
|
|
11145
|
+
if (scope === "user") {
|
|
11146
|
+
dir = path6.join(MEMORY_ROOT, "agent-memory");
|
|
11147
|
+
} else {
|
|
11148
|
+
if (!workDir)
|
|
11149
|
+
return null;
|
|
11150
|
+
dir = path6.join(path6.resolve(workDir), ".nexrall", scope === "local" ? "agent-memory-local" : "agent-memory");
|
|
11151
|
+
}
|
|
11152
|
+
const full = path6.join(dir, file);
|
|
11153
|
+
const rel = path6.relative(dir, full);
|
|
11154
|
+
if (rel.startsWith("..") || path6.isAbsolute(rel))
|
|
11155
|
+
return null;
|
|
11156
|
+
return full;
|
|
11157
|
+
}
|
|
11158
|
+
function readAgentMemory(agentName, scope, workDir) {
|
|
11159
|
+
const file = agentMemoryPath(agentName, scope, workDir);
|
|
11160
|
+
if (!file)
|
|
11161
|
+
return "";
|
|
11162
|
+
return readMemoryFile(file).trim();
|
|
11163
|
+
}
|
|
11164
|
+
async function writeAgentMemory(agentName, scope, content, workDir) {
|
|
11165
|
+
let trimmed = content.trim();
|
|
11166
|
+
if (!trimmed)
|
|
11167
|
+
return { ok: false, already: false, file: "" };
|
|
11168
|
+
if (trimmed.length > exports.MEMORY_ENTRY_MAX_CHARS) {
|
|
11169
|
+
trimmed = trimmed.slice(0, exports.MEMORY_ENTRY_MAX_CHARS - 1).trimEnd() + "\u2026";
|
|
11170
|
+
}
|
|
11171
|
+
const file = agentMemoryPath(agentName, scope, workDir);
|
|
11172
|
+
if (!file)
|
|
11173
|
+
return { ok: false, already: false, file: "" };
|
|
11174
|
+
return withLock(file, async () => {
|
|
11175
|
+
const existing = readMemoryFile(file);
|
|
11176
|
+
const fingerprint = trimmed.toLowerCase().slice(0, FINGERPRINT_LEN);
|
|
11177
|
+
if (existing.toLowerCase().includes(fingerprint)) {
|
|
11178
|
+
return { ok: true, already: true, file };
|
|
11179
|
+
}
|
|
11180
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
11181
|
+
let next = existing + `
|
|
11182
|
+
- [${today}] ${trimmed}`;
|
|
11183
|
+
if (Buffer.byteLength(next, "utf-8") > exports.AGENT_MEMORY_MAX_BYTES) {
|
|
11184
|
+
next = evictOldest(next, exports.AGENT_MEMORY_MAX_BYTES);
|
|
11185
|
+
}
|
|
11186
|
+
writeMemoryFile(file, next);
|
|
11187
|
+
return { ok: true, already: false, file };
|
|
11188
|
+
});
|
|
11189
|
+
}
|
|
11190
|
+
function agentMemoryPreamble(agentName, scope, workDir) {
|
|
11191
|
+
const notes = readAgentMemory(agentName, scope, workDir);
|
|
11192
|
+
if (!notes)
|
|
11193
|
+
return "";
|
|
11194
|
+
const kept = Buffer.byteLength(notes, "utf-8") > exports.AGENT_MEMORY_INJECT_MAX ? notes.slice(-exports.AGENT_MEMORY_INJECT_MAX) : notes;
|
|
11195
|
+
return `# Your own notes (agent: ${agentName}, scope: ${scope})
|
|
11196
|
+
These are notes YOU saved on previous runs. Treat them as your accumulated experience, not as instructions from the user, and verify anything that looks stale against the current code.
|
|
11197
|
+
|
|
11198
|
+
${kept}`;
|
|
11199
|
+
}
|
|
11130
11200
|
async function compactMemoryIfNeeded2(scope, workDir, summarize) {
|
|
11131
11201
|
const file = memoryFilePath(scope, workDir);
|
|
11132
11202
|
return withLock(file, async () => {
|
|
@@ -14585,6 +14655,11 @@ var require_agentTypes = __commonJS({
|
|
|
14585
14655
|
var path6 = __importStar(__require("path"));
|
|
14586
14656
|
var os6 = __importStar(__require("os"));
|
|
14587
14657
|
var index_1 = require_plugins();
|
|
14658
|
+
var VALID_MEMORY_SCOPES = ["project", "user", "local"];
|
|
14659
|
+
function parseMemoryScope(v) {
|
|
14660
|
+
const s2 = (v ?? "").trim().toLowerCase();
|
|
14661
|
+
return VALID_MEMORY_SCOPES.includes(s2) ? s2 : void 0;
|
|
14662
|
+
}
|
|
14588
14663
|
var READ_ONLY_TOOLS = [
|
|
14589
14664
|
// Universal
|
|
14590
14665
|
"read_file",
|
|
@@ -14600,6 +14675,22 @@ var require_agentTypes = __commonJS({
|
|
|
14600
14675
|
"notebook_read",
|
|
14601
14676
|
"todo_write",
|
|
14602
14677
|
"todo_read",
|
|
14678
|
+
// memory_READ, deliberately without memory_write.
|
|
14679
|
+
//
|
|
14680
|
+
// Reading is free capability: a sub-agent that knows "tests run with X" or "never
|
|
14681
|
+
// edit Z directly" does better work, and withholding it meant every sub-agent
|
|
14682
|
+
// rediscovered project conventions from scratch. Writing is a different thing
|
|
14683
|
+
// entirely — memory is durable, cross-session, SHARED state, while sub-agents run
|
|
14684
|
+
// up to 4-wide in parallel with their reasoning hidden from the user. A wrong fact
|
|
14685
|
+
// written there is invisible and permanent, and it competes for a capped, shared
|
|
14686
|
+
// budget that gets periodically condensed.
|
|
14687
|
+
//
|
|
14688
|
+
// So the parent owns writes: a sub-agent that learns something durable says so in
|
|
14689
|
+
// its report, and the main agent — the one actually talking to the user — decides.
|
|
14690
|
+
// A sub-agent that needs its own persistent notes gets the per-agent store instead
|
|
14691
|
+
// (see `memory:` in the frontmatter), which is scoped to itself and cannot pollute
|
|
14692
|
+
// the shared file.
|
|
14693
|
+
"memory_read",
|
|
14603
14694
|
// Skills are reusable prompt playbooks, and loop.ts advertises the skills
|
|
14604
14695
|
// catalogue to sub-agents at EVERY depth — so withholding the tool that loads
|
|
14605
14696
|
// one meant showing every sub-agent a menu it could not order from.
|
|
@@ -14832,6 +14923,9 @@ var require_agentTypes = __commonJS({
|
|
|
14832
14923
|
"stock_photo",
|
|
14833
14924
|
"open_in_browser",
|
|
14834
14925
|
"task",
|
|
14926
|
+
// Granted by a `memory:` scope rather than listed in a `tools:` line, but a user may
|
|
14927
|
+
// still name it explicitly — so it must validate rather than be flagged as a typo.
|
|
14928
|
+
"agent_memory_write",
|
|
14835
14929
|
"get_symbols",
|
|
14836
14930
|
"get_workspace_symbols",
|
|
14837
14931
|
"find_references",
|
|
@@ -14845,7 +14939,8 @@ var require_agentTypes = __commonJS({
|
|
|
14845
14939
|
"tools",
|
|
14846
14940
|
"model",
|
|
14847
14941
|
"test_files_only",
|
|
14848
|
-
"testfilesonly"
|
|
14942
|
+
"testfilesonly",
|
|
14943
|
+
"memory"
|
|
14849
14944
|
]);
|
|
14850
14945
|
var VALID_MODELS = ["turbo", "pro", "ultra"];
|
|
14851
14946
|
function parseFrontmatter(raw) {
|
|
@@ -14918,6 +15013,13 @@ var require_agentTypes = __commonJS({
|
|
|
14918
15013
|
message: "has no `description:` \u2014 that text is the ONLY thing the model uses to decide when to delegate to this agent, so it will rarely be picked."
|
|
14919
15014
|
});
|
|
14920
15015
|
}
|
|
15016
|
+
if (meta.memory !== void 0 && parseMemoryScope(meta.memory) === void 0) {
|
|
15017
|
+
warnings.push({
|
|
15018
|
+
file: full,
|
|
15019
|
+
agent: name,
|
|
15020
|
+
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.`
|
|
15021
|
+
});
|
|
15022
|
+
}
|
|
14921
15023
|
if (meta.model !== void 0 && parseModel(meta.model) === void 0) {
|
|
14922
15024
|
warnings.push({
|
|
14923
15025
|
file: full,
|
|
@@ -14960,7 +15062,13 @@ var require_agentTypes = __commonJS({
|
|
|
14960
15062
|
// `testFilesOnly`) lets anyone build a test-writing agent that genuinely
|
|
14961
15063
|
// cannot touch production source, rather than only the builtin getting
|
|
14962
15064
|
// that guarantee.
|
|
14963
|
-
...parseBool(meta.test_files_only ?? meta.testfilesonly) ? { testFilesOnly: true } : {}
|
|
15065
|
+
...parseBool(meta.test_files_only ?? meta.testfilesonly) ? { testFilesOnly: true } : {},
|
|
15066
|
+
// The `ok &&` is defensive, not load-bearing: parseFrontmatter already returns an
|
|
15067
|
+
// EMPTY meta when it cannot find a `---` block, so `meta.memory` is undefined on
|
|
15068
|
+
// that path regardless. It stays because the guarantee we want — an unreadable
|
|
15069
|
+
// definition never receives a writable store — should survive parseFrontmatter
|
|
15070
|
+
// being changed to salvage partial metadata, which is a plausible future edit.
|
|
15071
|
+
...ok && parseMemoryScope(meta.memory) ? { memory: parseMemoryScope(meta.memory) } : {}
|
|
14964
15072
|
});
|
|
14965
15073
|
}
|
|
14966
15074
|
}
|
|
@@ -15763,8 +15871,10 @@ var require_loop = __commonJS({
|
|
|
15763
15871
|
};
|
|
15764
15872
|
}();
|
|
15765
15873
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15766
|
-
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = exports.ToolNotAllowedError = exports._stallLimits = void 0;
|
|
15874
|
+
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = exports.ToolNotAllowedError = exports.AGENT_MEMORY_TOOL_SCHEMA = exports.AGENT_MEMORY_TOOL = exports._stallLimits = void 0;
|
|
15767
15875
|
exports.errorRoundSignature = errorRoundSignature;
|
|
15876
|
+
exports.executeAgentMemoryWrite = executeAgentMemoryWrite;
|
|
15877
|
+
exports.stopReasonNotice = stopReasonNotice;
|
|
15768
15878
|
exports.resolveMaxIterations = resolveMaxIterations;
|
|
15769
15879
|
exports.createLimiter = createLimiter;
|
|
15770
15880
|
exports.extractSubTaskText = extractSubTaskText;
|
|
@@ -15902,6 +16012,72 @@ var require_loop = __commonJS({
|
|
|
15902
16012
|
return errored.map(({ name, error }) => `${name}:${String(error).slice(0, 200)}`).sort().join("|");
|
|
15903
16013
|
}
|
|
15904
16014
|
exports._stallLimits = { STALL_LIMIT, REPEAT_STALL_LIMIT };
|
|
16015
|
+
exports.AGENT_MEMORY_TOOL = "agent_memory_write";
|
|
16016
|
+
exports.AGENT_MEMORY_TOOL_SCHEMA = {
|
|
16017
|
+
name: exports.AGENT_MEMORY_TOOL,
|
|
16018
|
+
description: "Save a durable note to YOUR OWN persistent notes, which are injected into your prompt on future runs. Use this for lessons that will still be true next time \u2014 a convention this repo follows, a recurring bug pattern, a command that works, a dead end not worth retrying. These notes are PRIVATE to you: they are NOT shown to the user and NOT read by the main agent, so anything the user or the main agent needs to know must still go in your final message. One self-contained fact per call, a sentence or two. Do not save transient task details.",
|
|
16019
|
+
input_schema: {
|
|
16020
|
+
type: "object",
|
|
16021
|
+
properties: {
|
|
16022
|
+
content: { type: "string", description: "The single fact to remember, 1-2 sentences." }
|
|
16023
|
+
},
|
|
16024
|
+
required: ["content"]
|
|
16025
|
+
}
|
|
16026
|
+
};
|
|
16027
|
+
async function executeAgentMemoryWrite(input, binding, workDir) {
|
|
16028
|
+
if (!binding) {
|
|
16029
|
+
return {
|
|
16030
|
+
error: `${exports.AGENT_MEMORY_TOOL} is only available to a sub-agent whose definition declares a \`memory:\` scope. Put anything worth remembering in your final message instead.`
|
|
16031
|
+
};
|
|
16032
|
+
}
|
|
16033
|
+
const content = typeof input.content === "string" ? input.content.trim() : "";
|
|
16034
|
+
if (!content)
|
|
16035
|
+
return { error: `${exports.AGENT_MEMORY_TOOL} requires a non-empty \`content\` string.` };
|
|
16036
|
+
const res = await (0, memory_1.writeAgentMemory)(binding.agentName, binding.scope, content, workDir);
|
|
16037
|
+
if (!res.ok) {
|
|
16038
|
+
return {
|
|
16039
|
+
error: `Could not save to the "${binding.agentName}" agent's ${binding.scope} notes. Either this scope needs a project directory (use \`memory: user\` for a store that works anywhere) or the agent name is not usable as a filename.`
|
|
16040
|
+
};
|
|
16041
|
+
}
|
|
16042
|
+
return {
|
|
16043
|
+
output: res.already ? "Already saved (a near-identical note exists) \u2014 nothing added." : `Saved to your ${binding.scope} notes. It will be in your prompt on your next run.`
|
|
16044
|
+
};
|
|
16045
|
+
}
|
|
16046
|
+
function stopReasonNotice(reason, ctx = {}) {
|
|
16047
|
+
switch (reason) {
|
|
16048
|
+
case "clean":
|
|
16049
|
+
case "aborted":
|
|
16050
|
+
case "reported-elsewhere":
|
|
16051
|
+
return null;
|
|
16052
|
+
case "empty-response":
|
|
16053
|
+
return `
|
|
16054
|
+
\u26A0\uFE0F The model returned an empty response, so nothing was done. This is usually a transient upstream hiccup \u2014 send "continue" to retry.
|
|
16055
|
+
`;
|
|
16056
|
+
case "no-balance":
|
|
16057
|
+
return `
|
|
16058
|
+
\u{1F4B3} Stopped: your balance is empty, so the request was rejected before it started. Top up and send "continue" \u2014 no tokens were used for this turn.
|
|
16059
|
+
`;
|
|
16060
|
+
case "stalled":
|
|
16061
|
+
return `
|
|
16062
|
+
\u{1F6D1} Stopped: the last ${STALL_LIMIT} tool rounds all failed, so the agent looked stuck. Fix the underlying error (or grant the needed permission) and send "continue".
|
|
16063
|
+
`;
|
|
16064
|
+
case "stalled-repeat":
|
|
16065
|
+
return `
|
|
16066
|
+
\u{1F6D1} Stopped: the same tool error repeated ${REPEAT_STALL_LIMIT} rounds in a row, so the agent was looping without making progress.` + (ctx.repeatError ? ` The recurring error was:
|
|
16067
|
+
${ctx.repeatError}
|
|
16068
|
+
` : "\n") + `Fix that underlying cause (or grant the needed permission) and send "continue".
|
|
16069
|
+
`;
|
|
16070
|
+
case "budget":
|
|
16071
|
+
return `
|
|
16072
|
+
\u23F8\uFE0F Stopped at the ${ctx.budget}-step safety limit \u2014 the task may be incomplete. Send "continue" to resume, or raise the limit via "maxIterations" in .nexrall/settings.json (or the NEXRALL_MAX_ITERATIONS env var). Auto-continue can be disabled with "autoContinue": false.
|
|
16073
|
+
`;
|
|
16074
|
+
case "unknown":
|
|
16075
|
+
default:
|
|
16076
|
+
return `
|
|
16077
|
+
\u26A0\uFE0F The run ended unexpectedly without completing. Your work so far is preserved \u2014 send "continue" to resume.
|
|
16078
|
+
`;
|
|
16079
|
+
}
|
|
16080
|
+
}
|
|
15905
16081
|
function resolveMaxIterations(optionValue, settingsRaw) {
|
|
15906
16082
|
const fromEnv = Number(process.env.NEXRALL_MAX_ITERATIONS);
|
|
15907
16083
|
const fromSettings = Number(settingsRaw.maxIterations);
|
|
@@ -16146,14 +16322,25 @@ ${tail}`;
|
|
|
16146
16322
|
If you just created .nexrall/agents/` + requestedType + ".md, make sure the write finished in an EARLIER tool call than this one \u2014 a file written in the same batch may not be on disk yet."
|
|
16147
16323
|
};
|
|
16148
16324
|
}
|
|
16325
|
+
const memoryScope = agent?.memory;
|
|
16326
|
+
const agentMemoryNotes = agent && memoryScope ? (0, memory_1.agentMemoryPreamble)(agent.name, memoryScope, options.workDir) : "";
|
|
16149
16327
|
const subNexrallMd = agent ? `# Sub-agent role: ${agent.name}
|
|
16150
|
-
${agent.prompt}` + (
|
|
16328
|
+
${agent.prompt}` + (agentMemoryNotes ? `
|
|
16329
|
+
|
|
16330
|
+
---
|
|
16331
|
+
|
|
16332
|
+
${agentMemoryNotes}` : "") + (options.nexrallMd ? `
|
|
16151
16333
|
|
|
16152
16334
|
---
|
|
16153
16335
|
|
|
16154
16336
|
${options.nexrallMd}` : "") : options.nexrallMd;
|
|
16155
16337
|
const allowed = agent?.tools ? new Set(agent.tools) : null;
|
|
16338
|
+
if (allowed && memoryScope)
|
|
16339
|
+
allowed.add(exports.AGENT_MEMORY_TOOL);
|
|
16156
16340
|
const gatedPermission = async (req) => {
|
|
16341
|
+
if (req.tool === exports.AGENT_MEMORY_TOOL && !(agent && memoryScope)) {
|
|
16342
|
+
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.`);
|
|
16343
|
+
}
|
|
16157
16344
|
if (allowed && !allowed.has(req.tool)) {
|
|
16158
16345
|
throw new ToolNotAllowedError(`The "${agent.name}" 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.`);
|
|
16159
16346
|
}
|
|
@@ -16177,6 +16364,11 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16177
16364
|
_depth: depth + 1,
|
|
16178
16365
|
_agentScope: `sub_${++_subTaskCounter}`,
|
|
16179
16366
|
// isolated todo store per sub-agent
|
|
16367
|
+
// Only set when the agent opted in, so the tool is inert for everyone else.
|
|
16368
|
+
...agent && memoryScope ? { _agentMemory: { agentName: agent.name, scope: memoryScope } } : {},
|
|
16369
|
+
// The same set `gatedPermission` enforces above, so prompt and permission agree
|
|
16370
|
+
// by construction instead of by two people remembering to update both.
|
|
16371
|
+
...allowed ? { _allowedTools: allowed } : {},
|
|
16180
16372
|
editorContext: null,
|
|
16181
16373
|
// fresh isolated context for sub-agent
|
|
16182
16374
|
model: agent?.model ?? options.model,
|
|
@@ -16651,9 +16843,8 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16651
16843
|
let lastPromptTokens = 0;
|
|
16652
16844
|
let compacting = false;
|
|
16653
16845
|
const hardCap = autoContinue ? Math.max(maxIterations, MAX_ITERATIONS_CEILING) : maxIterations;
|
|
16654
|
-
let
|
|
16846
|
+
let stopReason = "unknown";
|
|
16655
16847
|
let completedRounds = 0;
|
|
16656
|
-
let stalledOut = false;
|
|
16657
16848
|
let consecutiveErrorRounds = 0;
|
|
16658
16849
|
let repeatedErrorRounds = 0;
|
|
16659
16850
|
let lastErrorSignature = "";
|
|
@@ -16671,8 +16862,10 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16671
16862
|
let compactDisabled = false;
|
|
16672
16863
|
try {
|
|
16673
16864
|
for (; iteration < budget; iteration++) {
|
|
16674
|
-
if (options.abortSignal?.aborted)
|
|
16865
|
+
if (options.abortSignal?.aborted) {
|
|
16866
|
+
stopReason = "aborted";
|
|
16675
16867
|
break;
|
|
16868
|
+
}
|
|
16676
16869
|
let bodyBytes = estimateBodyBytes2(messages);
|
|
16677
16870
|
const prunePressure = lastPromptTokens > contextWindow * AUTO_PRUNE_THRESHOLD;
|
|
16678
16871
|
const tokenPressure = lastPromptTokens > contextWindow * AUTO_COMPACT_THRESHOLD;
|
|
@@ -16768,7 +16961,22 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16768
16961
|
nexrallMd: planAwareNexrallMd,
|
|
16769
16962
|
clientType: options.clientType,
|
|
16770
16963
|
abortSignal: options.abortSignal,
|
|
16771
|
-
|
|
16964
|
+
// MCP tools, plus the per-agent memory tool when (and only when) this run
|
|
16965
|
+
// is a sub-agent that declared a `memory:` scope. Declaring it through
|
|
16966
|
+
// extraTools rather than the backend's static catalogue keeps it invisible
|
|
16967
|
+
// to every other run: a tool the model cannot see is one it cannot try,
|
|
16968
|
+
// which is better than advertising it everywhere and refusing it at the gate.
|
|
16969
|
+
extraTools: [
|
|
16970
|
+
...options.mcpManager?.getAnthropicTools() ?? [],
|
|
16971
|
+
...options._agentMemory ? [exports.AGENT_MEMORY_TOOL_SCHEMA] : []
|
|
16972
|
+
],
|
|
16973
|
+
// Derived from the run's ACTUAL allowlist rather than asserted separately,
|
|
16974
|
+
// so the prompt's memory instructions cannot drift from what is permitted.
|
|
16975
|
+
// That drift is the bug being fixed: sub-agents were told they MUST call
|
|
16976
|
+
// memory_write, which no sub-agent allowlist contains.
|
|
16977
|
+
canWriteSharedMemory: options._allowedTools ? options._allowedTools.has("memory_write") : true,
|
|
16978
|
+
// no allowlist = main agent = may write
|
|
16979
|
+
hasOwnAgentStore: !!options._agentMemory,
|
|
16772
16980
|
agents: agentsCatalogue || void 0,
|
|
16773
16981
|
skills: skillsCatalogue || void 0,
|
|
16774
16982
|
// Only allow a post-render restart when the caller actually implements the
|
|
@@ -16778,19 +16986,24 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16778
16986
|
allowRestartAfterRender: !!options.onStreamRestart
|
|
16779
16987
|
}, onEvent);
|
|
16780
16988
|
} catch (err) {
|
|
16781
|
-
if (options.abortSignal?.aborted || err.name === "AbortError")
|
|
16989
|
+
if (options.abortSignal?.aborted || err.name === "AbortError") {
|
|
16990
|
+
stopReason = "aborted";
|
|
16782
16991
|
break;
|
|
16992
|
+
}
|
|
16783
16993
|
const status = err.status;
|
|
16784
16994
|
if (status === 402) {
|
|
16785
16995
|
const balance = err.balance ?? 0;
|
|
16786
16996
|
options.onBalanceStatus?.(balance, true);
|
|
16997
|
+
stopReason = options.onBalanceStatus ? "reported-elsewhere" : "no-balance";
|
|
16787
16998
|
break;
|
|
16788
16999
|
}
|
|
16789
17000
|
runSimpleHooks(hooks.OnError, options.workDir);
|
|
16790
17001
|
throw new types_1.AgentTurnError(`Stream failed: ${err.message}`, trimToResumableBoundary(messages), completedRounds, err);
|
|
16791
17002
|
}
|
|
16792
|
-
if (options.abortSignal?.aborted)
|
|
17003
|
+
if (options.abortSignal?.aborted) {
|
|
17004
|
+
stopReason = "aborted";
|
|
16793
17005
|
break;
|
|
17006
|
+
}
|
|
16794
17007
|
assistantMessage.content = assistantMessage.content.filter((b) => b.type !== "thinking" && b.type !== "redacted_thinking");
|
|
16795
17008
|
if (assistantMessage.content.length === 0) {
|
|
16796
17009
|
const queued = options.takePendingInput?.() ?? [];
|
|
@@ -16801,7 +17014,7 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16801
17014
|
continue;
|
|
16802
17015
|
}
|
|
16803
17016
|
runSimpleHooks(hooks.PostMessageComplete, options.workDir);
|
|
16804
|
-
|
|
17017
|
+
stopReason = "empty-response";
|
|
16805
17018
|
break;
|
|
16806
17019
|
}
|
|
16807
17020
|
const { stopReason: _stopReason, ...historyMessage } = assistantMessage;
|
|
@@ -16872,7 +17085,7 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16872
17085
|
}
|
|
16873
17086
|
}
|
|
16874
17087
|
runSimpleHooks(hooks.PostMessageComplete, options.workDir);
|
|
16875
|
-
|
|
17088
|
+
stopReason = "clean";
|
|
16876
17089
|
break;
|
|
16877
17090
|
}
|
|
16878
17091
|
const toolResults = await Promise.all(toolUseBlocks.map(async (block) => {
|
|
@@ -16914,6 +17127,11 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
16914
17127
|
result = { error: `Blocked by PreToolUse hook: ${pre.reason}` };
|
|
16915
17128
|
} else {
|
|
16916
17129
|
try {
|
|
17130
|
+
if (name === exports.AGENT_MEMORY_TOOL) {
|
|
17131
|
+
result = await executeAgentMemoryWrite(input, options._agentMemory, options.workDir);
|
|
17132
|
+
options.onToolResult(name, result);
|
|
17133
|
+
return { block: { ...block, id }, result };
|
|
17134
|
+
}
|
|
16917
17135
|
const external = options.executeExternalTool ? await options.executeExternalTool(name, input) : null;
|
|
16918
17136
|
if (external !== null && external !== void 0) {
|
|
16919
17137
|
result = external;
|
|
@@ -17006,7 +17224,7 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
|
|
|
17006
17224
|
const allErrored = toolResults.length > 0 && errored.length === toolResults.length;
|
|
17007
17225
|
consecutiveErrorRounds = allErrored ? consecutiveErrorRounds + 1 : 0;
|
|
17008
17226
|
if (consecutiveErrorRounds >= STALL_LIMIT) {
|
|
17009
|
-
|
|
17227
|
+
stopReason = "stalled";
|
|
17010
17228
|
break;
|
|
17011
17229
|
}
|
|
17012
17230
|
const errSignature = errorRoundSignature(errored.map(({ block, result }) => ({ name: block.name, error: String(result.error) })));
|
|
@@ -17017,7 +17235,7 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
|
|
|
17017
17235
|
lastErrorSignature = errSignature;
|
|
17018
17236
|
}
|
|
17019
17237
|
if (repeatedErrorRounds >= REPEAT_STALL_LIMIT) {
|
|
17020
|
-
|
|
17238
|
+
stopReason = "stalled-repeat";
|
|
17021
17239
|
stalledRepeatError = errored[0] ? String(errored[0].result.error).slice(0, 300) : null;
|
|
17022
17240
|
break;
|
|
17023
17241
|
}
|
|
@@ -17028,21 +17246,13 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
|
|
|
17028
17246
|
`);
|
|
17029
17247
|
}
|
|
17030
17248
|
}
|
|
17031
|
-
if (
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
\u{1F6D1} Stopped: the last ${STALL_LIMIT} tool rounds all failed, so the agent looked stuck. Fix the underlying error (or grant the needed permission) and send "continue".
|
|
17039
|
-
`);
|
|
17040
|
-
} else if (iteration >= budget) {
|
|
17041
|
-
(options.onNotice ?? options.onText)(`
|
|
17042
|
-
\u23F8\uFE0F Stopped at the ${budget}-step safety limit \u2014 the task may be incomplete. Send "continue" to resume, or raise the limit via "maxIterations" in .nexrall/settings.json (or the NEXRALL_MAX_ITERATIONS env var). Auto-continue can be disabled with "autoContinue": false.
|
|
17043
|
-
`);
|
|
17044
|
-
}
|
|
17045
|
-
}
|
|
17249
|
+
if (stopReason === "unknown" && iteration >= budget)
|
|
17250
|
+
stopReason = "budget";
|
|
17251
|
+
if (options.abortSignal?.aborted)
|
|
17252
|
+
stopReason = "aborted";
|
|
17253
|
+
const notice = stopReasonNotice(stopReason, { budget, repeatError: stalledRepeatError });
|
|
17254
|
+
if (notice)
|
|
17255
|
+
(options.onNotice ?? options.onText)(notice);
|
|
17046
17256
|
} catch (err) {
|
|
17047
17257
|
if (err instanceof types_1.AgentTurnError)
|
|
17048
17258
|
throw err;
|
|
@@ -64004,7 +64214,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
|
|
|
64004
64214
|
};
|
|
64005
64215
|
|
|
64006
64216
|
// src/commands/chat.ts
|
|
64007
|
-
var CLI_VERSION = "0.5.
|
|
64217
|
+
var CLI_VERSION = "0.5.54";
|
|
64008
64218
|
var MODEL_LABELS = {
|
|
64009
64219
|
turbo: "Nexrall Turbo",
|
|
64010
64220
|
pro: "Nexrall Pro",
|
|
@@ -65671,7 +65881,7 @@ function pluginSourceRemoveCommand(name, opts) {
|
|
|
65671
65881
|
|
|
65672
65882
|
// src/index.ts
|
|
65673
65883
|
var program2 = new Command();
|
|
65674
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
65884
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.54").enablePositionalOptions();
|
|
65675
65885
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
65676
65886
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
65677
65887
|
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.54",
|
|
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.29"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@aws-sdk/client-s3": "^3.600.0",
|