nexrall-code 0.5.161 → 0.5.163
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 +657 -44
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3186,7 +3186,7 @@ var require_auth = __commonJS({
|
|
|
3186
3186
|
}();
|
|
3187
3187
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3188
3188
|
exports2.saveAuth = saveAuth2;
|
|
3189
|
-
exports2.loadAuth =
|
|
3189
|
+
exports2.loadAuth = loadAuth3;
|
|
3190
3190
|
exports2.clearAuth = clearAuth2;
|
|
3191
3191
|
exports2.isAuthenticated = isAuthenticated4;
|
|
3192
3192
|
exports2.getToken = getToken2;
|
|
@@ -3207,7 +3207,7 @@ var require_auth = __commonJS({
|
|
|
3207
3207
|
ensureConfigDir();
|
|
3208
3208
|
fs27.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 384 });
|
|
3209
3209
|
}
|
|
3210
|
-
function
|
|
3210
|
+
function loadAuth3() {
|
|
3211
3211
|
const envToken = process.env.NEXRALL_TOKEN;
|
|
3212
3212
|
if (envToken && envToken.trim().length > 0) {
|
|
3213
3213
|
return { token: envToken.trim() };
|
|
@@ -3235,19 +3235,19 @@ var require_auth = __commonJS({
|
|
|
3235
3235
|
}
|
|
3236
3236
|
}
|
|
3237
3237
|
function isAuthenticated4() {
|
|
3238
|
-
const
|
|
3239
|
-
return
|
|
3238
|
+
const auth2 = loadAuth3();
|
|
3239
|
+
return auth2 !== null && auth2.token.length > 0;
|
|
3240
3240
|
}
|
|
3241
3241
|
function getToken2() {
|
|
3242
|
-
const
|
|
3243
|
-
return
|
|
3242
|
+
const auth2 = loadAuth3();
|
|
3243
|
+
return auth2?.token ?? null;
|
|
3244
3244
|
}
|
|
3245
3245
|
function getRefreshToken() {
|
|
3246
3246
|
const envToken = process.env.NEXRALL_TOKEN;
|
|
3247
3247
|
if (envToken && envToken.trim().length > 0)
|
|
3248
3248
|
return null;
|
|
3249
|
-
const
|
|
3250
|
-
const rt =
|
|
3249
|
+
const auth2 = loadAuth3();
|
|
3250
|
+
const rt = auth2?.refreshToken;
|
|
3251
3251
|
return typeof rt === "string" && rt.length > 0 ? rt : null;
|
|
3252
3252
|
}
|
|
3253
3253
|
function updateTokens(token, refreshToken) {
|
|
@@ -12139,11 +12139,11 @@ var require_agentTypes = __commonJS({
|
|
|
12139
12139
|
exports2.parseModel = parseModel;
|
|
12140
12140
|
exports2.parseAgentHooks = parseAgentHooks;
|
|
12141
12141
|
exports2.loadAgentTypes = loadAgentTypes;
|
|
12142
|
-
exports2.loadAgentTypesWithWarnings =
|
|
12143
|
-
exports2.knownToolNames =
|
|
12142
|
+
exports2.loadAgentTypesWithWarnings = loadAgentTypesWithWarnings3;
|
|
12143
|
+
exports2.knownToolNames = knownToolNames3;
|
|
12144
12144
|
exports2.builtinAgents = builtinAgents;
|
|
12145
12145
|
exports2.summariseAgents = summariseAgents;
|
|
12146
|
-
exports2.findAgentType =
|
|
12146
|
+
exports2.findAgentType = findAgentType2;
|
|
12147
12147
|
var miniYaml_1 = require_miniYaml();
|
|
12148
12148
|
var fs27 = __importStar(__require("fs"));
|
|
12149
12149
|
var path27 = __importStar(__require("path"));
|
|
@@ -12614,11 +12614,19 @@ var require_agentTypes = __commonJS({
|
|
|
12614
12614
|
throw new Error(`${where}: expected a list of hooks`);
|
|
12615
12615
|
return list.map((h2, i2) => {
|
|
12616
12616
|
const o = h2;
|
|
12617
|
+
const ms = typeof o.timeout_ms === "number" ? o.timeout_ms : typeof o.timeout === "number" ? o.timeout * 1e3 : void 0;
|
|
12618
|
+
const timeout = ms && ms > 0 ? { timeout_ms: ms } : {};
|
|
12619
|
+
if (o && o.type === "http" && typeof o.url === "string" && o.url.trim()) {
|
|
12620
|
+
if (!/^https?:\/\//i.test(o.url.trim()))
|
|
12621
|
+
throw new Error(`${where}[${i2}]: http hooks need an http(s) url`);
|
|
12622
|
+
const headers = o.headers && typeof o.headers === "object" && !Array.isArray(o.headers) ? o.headers : void 0;
|
|
12623
|
+
const allowed = Array.isArray(o.allowedEnvVars) ? o.allowedEnvVars.filter((v2) => typeof v2 === "string") : void 0;
|
|
12624
|
+
return { type: "http", url: o.url.trim(), ...headers ? { headers } : {}, ...allowed ? { allowedEnvVars: allowed } : {}, ...timeout };
|
|
12625
|
+
}
|
|
12617
12626
|
if (!o || o.type !== "command" || typeof o.command !== "string" || !o.command.trim()) {
|
|
12618
|
-
throw new Error(`${where}[${i2}]: each hook needs type: command and a command`);
|
|
12627
|
+
throw new Error(`${where}[${i2}]: each hook needs type: command and a command, or type: http and a url`);
|
|
12619
12628
|
}
|
|
12620
|
-
|
|
12621
|
-
return { type: "command", command: o.command, ...ms && ms > 0 ? { timeout_ms: ms } : {} };
|
|
12629
|
+
return { type: "command", command: o.command, ...timeout };
|
|
12622
12630
|
});
|
|
12623
12631
|
};
|
|
12624
12632
|
const entries = (list, ev) => {
|
|
@@ -12677,9 +12685,9 @@ var require_agentTypes = __commonJS({
|
|
|
12677
12685
|
return out;
|
|
12678
12686
|
}
|
|
12679
12687
|
function loadAgentTypes(workDir) {
|
|
12680
|
-
return
|
|
12688
|
+
return loadAgentTypesWithWarnings3(workDir).types;
|
|
12681
12689
|
}
|
|
12682
|
-
function
|
|
12690
|
+
function loadAgentTypesWithWarnings3(workDir, extra = []) {
|
|
12683
12691
|
const out = /* @__PURE__ */ new Map();
|
|
12684
12692
|
const warnings = [];
|
|
12685
12693
|
loadDir(path27.join(workDir, ".nexrall", "agents"), "project", out, warnings);
|
|
@@ -12706,7 +12714,7 @@ var require_agentTypes = __commonJS({
|
|
|
12706
12714
|
});
|
|
12707
12715
|
return { types: types3, warnings };
|
|
12708
12716
|
}
|
|
12709
|
-
function
|
|
12717
|
+
function knownToolNames3() {
|
|
12710
12718
|
return [...KNOWN_TOOL_NAMES].sort();
|
|
12711
12719
|
}
|
|
12712
12720
|
function builtinAgents() {
|
|
@@ -12734,7 +12742,7 @@ var require_agentTypes = __commonJS({
|
|
|
12734
12742
|
"write_xlsx",
|
|
12735
12743
|
"write_pptx"
|
|
12736
12744
|
]);
|
|
12737
|
-
function
|
|
12745
|
+
function findAgentType2(types3, name) {
|
|
12738
12746
|
if (!name)
|
|
12739
12747
|
return void 0;
|
|
12740
12748
|
const want = name.trim().toLowerCase();
|
|
@@ -104672,6 +104680,8 @@ var require_rules = __commonJS({
|
|
|
104672
104680
|
}();
|
|
104673
104681
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
104674
104682
|
exports2.setSessionRules = setSessionRules2;
|
|
104683
|
+
exports2.setSessionSettings = setSessionSettings2;
|
|
104684
|
+
exports2.getSessionSettings = getSessionSettings;
|
|
104675
104685
|
exports2.loadSettings = loadSettings5;
|
|
104676
104686
|
exports2.evaluatePermission = evaluatePermission4;
|
|
104677
104687
|
var fs27 = __importStar(__require("fs"));
|
|
@@ -104700,6 +104710,13 @@ var require_rules = __commonJS({
|
|
|
104700
104710
|
function setSessionRules2(r2) {
|
|
104701
104711
|
sessionRules = { allow: [...r2.allow ?? []], ask: [...r2.ask ?? []], deny: [...r2.deny ?? []] };
|
|
104702
104712
|
}
|
|
104713
|
+
var sessionSettings = null;
|
|
104714
|
+
function setSessionSettings2(obj2) {
|
|
104715
|
+
sessionSettings = obj2;
|
|
104716
|
+
}
|
|
104717
|
+
function getSessionSettings() {
|
|
104718
|
+
return sessionSettings;
|
|
104719
|
+
}
|
|
104703
104720
|
function loadSettings5(workDir) {
|
|
104704
104721
|
const tiers = [
|
|
104705
104722
|
managedSettingsPath(),
|
|
@@ -104719,6 +104736,13 @@ var require_rules = __commonJS({
|
|
|
104719
104736
|
merged.ask.push(...r2.ask);
|
|
104720
104737
|
merged.deny.push(...r2.deny);
|
|
104721
104738
|
}
|
|
104739
|
+
if (sessionSettings) {
|
|
104740
|
+
Object.assign(raw, sessionSettings);
|
|
104741
|
+
const r2 = extractRules(sessionSettings);
|
|
104742
|
+
merged.allow.push(...r2.allow);
|
|
104743
|
+
merged.ask.push(...r2.ask);
|
|
104744
|
+
merged.deny.push(...r2.deny);
|
|
104745
|
+
}
|
|
104722
104746
|
merged.allow.push(...sessionRules.allow);
|
|
104723
104747
|
merged.ask.push(...sessionRules.ask);
|
|
104724
104748
|
merged.deny.push(...sessionRules.deny);
|
|
@@ -106887,7 +106911,9 @@ var require_loop = __commonJS({
|
|
|
106887
106911
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
106888
106912
|
exports2.VERIFY_CMD_RE = exports2.WRITE_TOOL_NAMES = exports2.ToolNotAllowedError = exports2.AGENT_MEMORY_TOOL_SCHEMA = exports2.AGENT_MEMORY_TOOL = exports2._stallLimits = exports2._emptyTurnRetry = exports2.bashNeedsRepoLock = void 0;
|
|
106889
106913
|
exports2.runLifecycleHooks = runLifecycleHooks;
|
|
106914
|
+
exports2.runPermissionRequestHooks = runPermissionRequestHooks2;
|
|
106890
106915
|
exports2.fireNotificationHooks = fireNotificationHooks2;
|
|
106916
|
+
exports2.fireManualPostCompactHook = fireManualPostCompactHook2;
|
|
106891
106917
|
exports2.fireManualPreCompactHook = fireManualPreCompactHook2;
|
|
106892
106918
|
exports2.emptyTurnBackoffMs = emptyTurnBackoffMs;
|
|
106893
106919
|
exports2.shouldRetryEmptyTurn = shouldRetryEmptyTurn;
|
|
@@ -106981,6 +107007,15 @@ var require_loop = __commonJS({
|
|
|
106981
107007
|
fromSettings = JSON.parse(fs27.readFileSync(p, "utf-8")).hooks ?? {};
|
|
106982
107008
|
} catch {
|
|
106983
107009
|
}
|
|
107010
|
+
const sess = (0, rules_1.getSessionSettings)()?.hooks;
|
|
107011
|
+
if (sess && typeof sess === "object") {
|
|
107012
|
+
const base = { ...fromSettings };
|
|
107013
|
+
for (const [phase, list] of Object.entries(sess)) {
|
|
107014
|
+
if (Array.isArray(list))
|
|
107015
|
+
base[phase] = [...base[phase] ?? [], ...list];
|
|
107016
|
+
}
|
|
107017
|
+
fromSettings = base;
|
|
107018
|
+
}
|
|
106984
107019
|
const fromPlugins = (0, index_1.pluginHooks)(workDir);
|
|
106985
107020
|
const merged = { ...fromSettings };
|
|
106986
107021
|
for (const phase of Object.keys(fromPlugins)) {
|
|
@@ -107036,6 +107071,42 @@ var require_loop = __commonJS({
|
|
|
107036
107071
|
child.stdin?.end(opts.input ?? "");
|
|
107037
107072
|
});
|
|
107038
107073
|
}
|
|
107074
|
+
async function httpHook(hook, payload, timeoutMs) {
|
|
107075
|
+
const url = String(hook.url ?? "");
|
|
107076
|
+
if (!/^https?:\/\//i.test(url))
|
|
107077
|
+
return { status: 1, stdout: "", stderr: `hook url must be http(s): ${url}` };
|
|
107078
|
+
const headers = { "content-type": "application/json" };
|
|
107079
|
+
const allowed = new Set(hook.allowedEnvVars ?? []);
|
|
107080
|
+
for (const [k, v] of Object.entries(hook.headers ?? {})) {
|
|
107081
|
+
headers[k] = String(v).replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_m, a1, a2) => allowed.has(a1 ?? a2) ? process.env[a1 ?? a2] ?? "" : "");
|
|
107082
|
+
}
|
|
107083
|
+
try {
|
|
107084
|
+
const res = await fetch(url, { method: "POST", headers, body: payload, signal: AbortSignal.timeout(timeoutMs) });
|
|
107085
|
+
const text = (await res.text()).trim();
|
|
107086
|
+
if (res.status < 200 || res.status >= 300) {
|
|
107087
|
+
return { status: 1, stdout: "", stderr: `hook returned HTTP ${res.status}` };
|
|
107088
|
+
}
|
|
107089
|
+
if (!text)
|
|
107090
|
+
return { status: 0, stdout: "", stderr: "" };
|
|
107091
|
+
if (text.startsWith("{"))
|
|
107092
|
+
return { status: 0, stdout: text, stderr: "" };
|
|
107093
|
+
return { status: 1, stdout: "", stderr: "hook returned a non-JSON body" };
|
|
107094
|
+
} catch (err) {
|
|
107095
|
+
return { status: 1, stdout: "", stderr: `hook request failed: ${err.message}` };
|
|
107096
|
+
}
|
|
107097
|
+
}
|
|
107098
|
+
async function runHookHandler(hook, payload, opts) {
|
|
107099
|
+
if (hook.type === "http")
|
|
107100
|
+
return httpHook(hook, payload, opts.timeoutMs);
|
|
107101
|
+
return spawnHook(String(hook.command ?? ""), { cwd: opts.cwd, timeout: opts.timeoutMs, input: payload, env: opts.env });
|
|
107102
|
+
}
|
|
107103
|
+
function hookTimeoutMs(hook, fallbackMs = 6e4) {
|
|
107104
|
+
if (typeof hook.timeout_ms === "number" && hook.timeout_ms > 0)
|
|
107105
|
+
return Math.min(hook.timeout_ms, 6e5);
|
|
107106
|
+
if (typeof hook.timeout === "number" && hook.timeout > 0)
|
|
107107
|
+
return Math.min(hook.timeout * 1e3, 6e5);
|
|
107108
|
+
return fallbackMs;
|
|
107109
|
+
}
|
|
107039
107110
|
function hookMatches(matcher, toolName) {
|
|
107040
107111
|
if (!matcher || matcher === "*")
|
|
107041
107112
|
return true;
|
|
@@ -107058,13 +107129,16 @@ var require_loop = __commonJS({
|
|
|
107058
107129
|
if (!hookMatches(entry.matcher, toolName))
|
|
107059
107130
|
continue;
|
|
107060
107131
|
for (const hook of entry.hooks ?? []) {
|
|
107061
|
-
if (hook.type !== "command"
|
|
107132
|
+
if (hook.type !== "command" && hook.type !== "http")
|
|
107133
|
+
continue;
|
|
107134
|
+
if (hook.type === "command" && !hook.command)
|
|
107135
|
+
continue;
|
|
107136
|
+
if (hook.type === "http" && !hook.url)
|
|
107062
107137
|
continue;
|
|
107063
|
-
const hookTimeout =
|
|
107064
|
-
const r2 = await
|
|
107138
|
+
const hookTimeout = hookTimeoutMs(hook, 6e4);
|
|
107139
|
+
const r2 = await runHookHandler(hook, payload, {
|
|
107065
107140
|
cwd: workDir,
|
|
107066
|
-
|
|
107067
|
-
input: payload,
|
|
107141
|
+
timeoutMs: hookTimeout,
|
|
107068
107142
|
env: {
|
|
107069
107143
|
...process.env,
|
|
107070
107144
|
NEXRALL_TOOL_NAME: toolName,
|
|
@@ -107099,9 +107173,13 @@ var require_loop = __commonJS({
|
|
|
107099
107173
|
if (!defs?.length)
|
|
107100
107174
|
return;
|
|
107101
107175
|
for (const hook of defs) {
|
|
107102
|
-
if (hook.type
|
|
107103
|
-
|
|
107104
|
-
|
|
107176
|
+
if (hook.type !== "command" && hook.type !== "http")
|
|
107177
|
+
continue;
|
|
107178
|
+
if (hook.type === "command" && !hook.command)
|
|
107179
|
+
continue;
|
|
107180
|
+
if (hook.type === "http" && !hook.url)
|
|
107181
|
+
continue;
|
|
107182
|
+
await runHookHandler(hook, "", { cwd: workDir, timeoutMs: hookTimeoutMs(hook, 1e4), env: extraEnv ? { ...process.env, ...extraEnv } : void 0 });
|
|
107105
107183
|
}
|
|
107106
107184
|
}
|
|
107107
107185
|
function flattenHookDefs(items, matchValue) {
|
|
@@ -107112,7 +107190,7 @@ var require_loop = __commonJS({
|
|
|
107112
107190
|
if (m2 && m2 !== "*" && matchValue !== void 0 && !m2.split("|").map((x2) => x2.trim()).includes(matchValue))
|
|
107113
107191
|
continue;
|
|
107114
107192
|
out.push(...it.hooks);
|
|
107115
|
-
} else if (it && it.type === "command")
|
|
107193
|
+
} else if (it && (it.type === "command" || it.type === "http"))
|
|
107116
107194
|
out.push(it);
|
|
107117
107195
|
}
|
|
107118
107196
|
return out;
|
|
@@ -107124,14 +107202,27 @@ var require_loop = __commonJS({
|
|
|
107124
107202
|
return outcome;
|
|
107125
107203
|
const input = JSON.stringify({ hook_event_name: event, cwd: workDir, ...payload });
|
|
107126
107204
|
for (const hook of defs) {
|
|
107127
|
-
if (
|
|
107205
|
+
if (hook.type !== "command" && hook.type !== "http")
|
|
107206
|
+
continue;
|
|
107207
|
+
if (hook.type === "command" && !hook.command)
|
|
107128
107208
|
continue;
|
|
107129
|
-
|
|
107130
|
-
|
|
107209
|
+
if (hook.type === "http" && !hook.url)
|
|
107210
|
+
continue;
|
|
107211
|
+
const timeout = hookTimeoutMs(hook, 3e4);
|
|
107212
|
+
const r2 = await runHookHandler(hook, input, { cwd: workDir, timeoutMs: timeout, env: { ...process.env, NEXRALL_HOOK_PHASE: event } });
|
|
107131
107213
|
const out = (r2.stdout ?? "").toString().trim();
|
|
107132
107214
|
if (out.startsWith("{")) {
|
|
107133
107215
|
try {
|
|
107134
107216
|
const j = JSON.parse(out);
|
|
107217
|
+
if (event === "PermissionRequest") {
|
|
107218
|
+
const d = j.hookSpecificOutput?.decision?.behavior ?? (typeof j.decision === "string" ? j.decision : j.decision?.behavior);
|
|
107219
|
+
if (d === "allow" || d === "approve")
|
|
107220
|
+
outcome.allow = true;
|
|
107221
|
+
else if (d === "deny" || d === "block") {
|
|
107222
|
+
outcome.block = true;
|
|
107223
|
+
outcome.reason = j.reason ?? j.hookSpecificOutput?.decision?.message ?? outcome.reason ?? "Denied by PermissionRequest hook";
|
|
107224
|
+
}
|
|
107225
|
+
}
|
|
107135
107226
|
if (j.decision === "block") {
|
|
107136
107227
|
outcome.block = true;
|
|
107137
107228
|
outcome.reason = j.reason ?? outcome.reason ?? `Blocked by ${event} hook`;
|
|
@@ -107150,6 +107241,20 @@ var require_loop = __commonJS({
|
|
|
107150
107241
|
}
|
|
107151
107242
|
return outcome;
|
|
107152
107243
|
}
|
|
107244
|
+
async function runPermissionRequestHooks2(workDir, tool, input, sessionId = "") {
|
|
107245
|
+
try {
|
|
107246
|
+
const items = loadHooks(workDir).PermissionRequest;
|
|
107247
|
+
if (!items?.length)
|
|
107248
|
+
return { answer: null };
|
|
107249
|
+
const o = await runLifecycleHooks(items, "PermissionRequest", workDir, { session_id: sessionId, tool_name: tool, tool_input: input }, tool);
|
|
107250
|
+
if (o.block)
|
|
107251
|
+
return { answer: "deny", reason: o.reason };
|
|
107252
|
+
if (o.allow)
|
|
107253
|
+
return { answer: "allow" };
|
|
107254
|
+
} catch {
|
|
107255
|
+
}
|
|
107256
|
+
return { answer: null };
|
|
107257
|
+
}
|
|
107153
107258
|
function fireNotificationHooks2(workDir, message, type = "permission_prompt") {
|
|
107154
107259
|
try {
|
|
107155
107260
|
const items = loadHooks(workDir).Notification;
|
|
@@ -107160,6 +107265,15 @@ var require_loop = __commonJS({
|
|
|
107160
107265
|
} catch {
|
|
107161
107266
|
}
|
|
107162
107267
|
}
|
|
107268
|
+
async function fireManualPostCompactHook2(workDir, sessionId, messagesSummarized) {
|
|
107269
|
+
try {
|
|
107270
|
+
const items = loadHooks(workDir).PostCompact;
|
|
107271
|
+
if (!items?.length)
|
|
107272
|
+
return;
|
|
107273
|
+
await runLifecycleHooks(items, "PostCompact", workDir, { trigger: "manual", session_id: sessionId, messages_summarized: messagesSummarized }, "manual");
|
|
107274
|
+
} catch {
|
|
107275
|
+
}
|
|
107276
|
+
}
|
|
107163
107277
|
async function fireManualPreCompactHook2(workDir, sessionId, messagesToSummarize) {
|
|
107164
107278
|
try {
|
|
107165
107279
|
const items = loadHooks(workDir).PreCompact;
|
|
@@ -107971,6 +108085,23 @@ ${stats}${isoNote}`;
|
|
|
107971
108085
|
try {
|
|
107972
108086
|
if (started)
|
|
107973
108087
|
started.value = true;
|
|
108088
|
+
{
|
|
108089
|
+
const subAgentName = agent?.name ?? (typeof input.subagent_type === "string" && input.subagent_type ? input.subagent_type : "general-purpose");
|
|
108090
|
+
const startItems = loadHooks(options.workDir).SubagentStart;
|
|
108091
|
+
if (startItems?.length) {
|
|
108092
|
+
const o = await runLifecycleHooks(startItems, "SubagentStart", options.workDir, { session_id: options.sessionId ?? "", agent_type: subAgentName, description: String(input.description ?? "") }, subAgentName).catch(() => ({ block: false }));
|
|
108093
|
+
if (o.context) {
|
|
108094
|
+
const last = subMessages[subMessages.length - 1];
|
|
108095
|
+
if (last && Array.isArray(last.content)) {
|
|
108096
|
+
subMessages[subMessages.length - 1] = { ...last, content: [...last.content, { type: "text", text: `
|
|
108097
|
+
|
|
108098
|
+
<hook-context>
|
|
108099
|
+
${o.context}
|
|
108100
|
+
</hook-context>` }] };
|
|
108101
|
+
}
|
|
108102
|
+
}
|
|
108103
|
+
}
|
|
108104
|
+
}
|
|
107974
108105
|
const childRun = runAgentLoop2(subMessages, {
|
|
107975
108106
|
...options,
|
|
107976
108107
|
_depth: depth + 1,
|
|
@@ -108522,6 +108653,11 @@ Do NOT call any tools and do NOT continue the task. Reply with text only: a conc
|
|
|
108522
108653
|
}
|
|
108523
108654
|
const toSummarize = messages.slice(0, cut);
|
|
108524
108655
|
const kept = messages.slice(cut);
|
|
108656
|
+
const firePostCompact = async () => {
|
|
108657
|
+
if ((options._depth ?? 0) !== 0)
|
|
108658
|
+
return;
|
|
108659
|
+
await runLifecycleHooks(loadHooks(options.workDir).PostCompact, "PostCompact", options.workDir, { trigger: "auto", session_id: options.sessionId ?? "", messages_summarized: cut }, "auto").catch(() => void 0);
|
|
108660
|
+
};
|
|
108525
108661
|
const originalTask = originalTaskText(toSummarize);
|
|
108526
108662
|
const summaryPrompt = `Summarize this coding-session transcript into concise bullet points the assistant needs to continue the work: key decisions, files changed (and how), commands run, unresolved problems, and user preferences. Max 400 words.
|
|
108527
108663
|
|
|
@@ -108590,6 +108726,7 @@ ${taskBlock}${ledgerBlock}Summary of the earlier conversation so far:
|
|
|
108590
108726
|
${summary}
|
|
108591
108727
|
|
|
108592
108728
|
Continue the work from here.` }] });
|
|
108729
|
+
await firePostCompact();
|
|
108593
108730
|
return true;
|
|
108594
108731
|
}
|
|
108595
108732
|
async function maybeCompactMemory(scope, options) {
|
|
@@ -108766,7 +108903,7 @@ Continue the work from here.` }] });
|
|
|
108766
108903
|
async function runAgentLoop2(initialMessages, options) {
|
|
108767
108904
|
const messages = [...initialMessages];
|
|
108768
108905
|
const model = options.model ?? "turbo";
|
|
108769
|
-
const hooks = withAgentHooks(loadHooks(options.workDir), options._agentHooks);
|
|
108906
|
+
const hooks = options.disableHooks ? withAgentHooks({}, options._agentHooks) : withAgentHooks(loadHooks(options.workDir), options._agentHooks);
|
|
108770
108907
|
const depth = options._depth ?? 0;
|
|
108771
108908
|
const agentScope = options._agentScope ?? "root";
|
|
108772
108909
|
const isMainAgent = depth === 0;
|
|
@@ -109303,6 +109440,15 @@ ${ctx}
|
|
|
109303
109440
|
void maybeCompactMemory(scope, options);
|
|
109304
109441
|
}
|
|
109305
109442
|
const post = await runToolHooks(hooks.PostToolUse, "PostToolUse", name, input, options.workDir, result);
|
|
109443
|
+
if (result.error !== void 0 && (hooks.PostToolUseFailure?.length ?? 0) > 0) {
|
|
109444
|
+
const f3 = await runLifecycleHooks(hooks.PostToolUseFailure, "PostToolUseFailure", options.workDir, { session_id: options.sessionId ?? "", tool_name: name, tool_input: input, error: String(result.error).slice(0, 4e3) }, name).catch(() => ({ block: false }));
|
|
109445
|
+
if (f3.context)
|
|
109446
|
+
post.context = [post.context, f3.context].filter(Boolean).join("\n");
|
|
109447
|
+
if (f3.block) {
|
|
109448
|
+
post.block = true;
|
|
109449
|
+
post.reason = [post.reason, f3.reason].filter(Boolean).join("; ");
|
|
109450
|
+
}
|
|
109451
|
+
}
|
|
109306
109452
|
const injected = [pre.context, post.context].filter(Boolean).join("\n");
|
|
109307
109453
|
if (injected) {
|
|
109308
109454
|
if (result.error !== void 0)
|
|
@@ -109430,6 +109576,9 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
|
|
|
109430
109576
|
throw err;
|
|
109431
109577
|
throw new types_1.AgentTurnError(err?.message || "The turn ended unexpectedly", trimToResumableBoundary(messages), completedRounds, err);
|
|
109432
109578
|
} finally {
|
|
109579
|
+
if (depth === 0 && (hooks.SessionEnd?.length ?? 0) > 0) {
|
|
109580
|
+
await runLifecycleHooks(hooks.SessionEnd, "SessionEnd", options.workDir, { session_id: options.sessionId ?? "", reason: stopReason }, stopReason).catch(() => void 0);
|
|
109581
|
+
}
|
|
109433
109582
|
if (depth === 0)
|
|
109434
109583
|
await runSimpleHooks(hooks.OnStop, options.workDir);
|
|
109435
109584
|
else
|
|
@@ -151573,6 +151722,13 @@ async function requestPermissionInner(req) {
|
|
|
151573
151722
|
console.error(paint.yellow(` \u2298 Plan mode \u2014 refused ${tool} (read-only until you switch mode).`));
|
|
151574
151723
|
return false;
|
|
151575
151724
|
}
|
|
151725
|
+
const hook = await (0, import_code_core5.runPermissionRequestHooks)(_workDir, tool, input);
|
|
151726
|
+
if (hook.answer === "allow")
|
|
151727
|
+
return true;
|
|
151728
|
+
if (hook.answer === "deny") {
|
|
151729
|
+
console.error(paint.red(` \u2716 Denied by PermissionRequest hook: ${hook.reason ?? tool}`));
|
|
151730
|
+
return false;
|
|
151731
|
+
}
|
|
151576
151732
|
return serialisePrompt(() => askFlow(req));
|
|
151577
151733
|
}
|
|
151578
151734
|
async function requestPermission(req) {
|
|
@@ -151908,6 +152064,8 @@ function checkHeadlessOnly(f3) {
|
|
|
151908
152064
|
return "--max-budget-usd only works with --output-format (a one-shot, non-interactive run).";
|
|
151909
152065
|
if (f3.noSessionPersistence)
|
|
151910
152066
|
return "--no-session-persistence only works with --output-format (a one-shot, non-interactive run).";
|
|
152067
|
+
if (f3.maxTurns !== void 0)
|
|
152068
|
+
return "--max-turns only works with --output-format (a one-shot, non-interactive run).";
|
|
151911
152069
|
return null;
|
|
151912
152070
|
}
|
|
151913
152071
|
function headlessVerdict(i2) {
|
|
@@ -152076,6 +152234,230 @@ function validateJsonSchema(value, schema, at = "$") {
|
|
|
152076
152234
|
}
|
|
152077
152235
|
return errs;
|
|
152078
152236
|
}
|
|
152237
|
+
function parseMaxTurns(raw) {
|
|
152238
|
+
const s2 = String(raw).trim();
|
|
152239
|
+
const n = Number(s2);
|
|
152240
|
+
if (!/^\d+$/.test(s2) || !Number.isSafeInteger(n) || n < 1) {
|
|
152241
|
+
return { ok: false, error: `Invalid --max-turns "${raw}". Use a whole number of 1 or more, e.g. 10` };
|
|
152242
|
+
}
|
|
152243
|
+
return { ok: true, value: n };
|
|
152244
|
+
}
|
|
152245
|
+
function readPromptFile(file, cwd2, flag = "--append-system-prompt-file") {
|
|
152246
|
+
const abs = path7.resolve(cwd2, file);
|
|
152247
|
+
let text;
|
|
152248
|
+
try {
|
|
152249
|
+
text = fs6.readFileSync(abs, "utf-8");
|
|
152250
|
+
} catch {
|
|
152251
|
+
return { ok: false, error: `${flag}: cannot read "${file}"` };
|
|
152252
|
+
}
|
|
152253
|
+
if (!text.trim())
|
|
152254
|
+
return { ok: false, error: `${flag}: "${file}" is empty` };
|
|
152255
|
+
return { ok: true, value: text.trim() };
|
|
152256
|
+
}
|
|
152257
|
+
var CLAUDE_TOOL_ALIASES = {
|
|
152258
|
+
Read: ["read_file", "list_directory", "search_files", "glob", "notebook_read"],
|
|
152259
|
+
Edit: ["edit_file", "multi_edit", "notebook_edit"],
|
|
152260
|
+
Write: ["write_file", "write_docx", "write_xlsx", "write_pptx"],
|
|
152261
|
+
Bash: ["bash", "bash_output", "kill_shell"],
|
|
152262
|
+
Glob: ["glob"],
|
|
152263
|
+
Grep: ["search_files"],
|
|
152264
|
+
WebFetch: ["fetch_url"],
|
|
152265
|
+
WebSearch: ["web_search"],
|
|
152266
|
+
Task: ["task"],
|
|
152267
|
+
TodoWrite: ["todo_write", "todo_read"],
|
|
152268
|
+
NotebookEdit: ["notebook_edit"]
|
|
152269
|
+
};
|
|
152270
|
+
function parseToolsFlag(raw, known) {
|
|
152271
|
+
const out = /* @__PURE__ */ new Set();
|
|
152272
|
+
const trimmed = String(raw).trim();
|
|
152273
|
+
if (trimmed === "")
|
|
152274
|
+
return { ok: true, value: [] };
|
|
152275
|
+
if (trimmed.toLowerCase() === "default")
|
|
152276
|
+
return { ok: true, value: [...known] };
|
|
152277
|
+
for (const piece of parseToolList(trimmed)) {
|
|
152278
|
+
if (CLAUDE_TOOL_ALIASES[piece]) {
|
|
152279
|
+
CLAUDE_TOOL_ALIASES[piece].forEach((t2) => out.add(t2));
|
|
152280
|
+
continue;
|
|
152281
|
+
}
|
|
152282
|
+
if (known.includes(piece) || piece.startsWith("mcp__")) {
|
|
152283
|
+
out.add(piece);
|
|
152284
|
+
continue;
|
|
152285
|
+
}
|
|
152286
|
+
return { ok: false, error: `--tools: unknown tool "${piece}". Known: ${[...Object.keys(CLAUDE_TOOL_ALIASES), ...known].join(", ")}` };
|
|
152287
|
+
}
|
|
152288
|
+
return { ok: true, value: [...out] };
|
|
152289
|
+
}
|
|
152290
|
+
function parseSessionId(raw) {
|
|
152291
|
+
const s2 = String(raw).trim();
|
|
152292
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(s2)) {
|
|
152293
|
+
return { ok: false, error: `Invalid --session-id "${raw}". Use letters, digits, "-" or "_" (max 64), e.g. a UUID` };
|
|
152294
|
+
}
|
|
152295
|
+
return { ok: true, value: s2 };
|
|
152296
|
+
}
|
|
152297
|
+
function parseSessionName(raw) {
|
|
152298
|
+
const s2 = String(raw).replace(/\s+/g, " ").trim();
|
|
152299
|
+
if (!s2)
|
|
152300
|
+
return { ok: false, error: "Invalid --name: it is empty" };
|
|
152301
|
+
return { ok: true, value: s2.slice(0, 80) };
|
|
152302
|
+
}
|
|
152303
|
+
function checkSessionIdentityFlags(f3) {
|
|
152304
|
+
const resuming = f3.resume !== void 0 || !!f3.continue;
|
|
152305
|
+
if (f3.fork && !resuming)
|
|
152306
|
+
return "--fork-session needs --resume <id> or --continue (there is nothing to fork otherwise).";
|
|
152307
|
+
if (f3.sessionId && resuming && !f3.fork)
|
|
152308
|
+
return "--session-id starts a new conversation; with --resume/--continue add --fork-session to branch it under that id.";
|
|
152309
|
+
return null;
|
|
152310
|
+
}
|
|
152311
|
+
function matchSessionByName(sessions, name) {
|
|
152312
|
+
const want = name.trim().toLowerCase();
|
|
152313
|
+
const hits = sessions.filter((s2) => s2.title.trim().toLowerCase() === want).sort((a, b) => b.updatedAt - a.updatedAt);
|
|
152314
|
+
return hits[0] ?? null;
|
|
152315
|
+
}
|
|
152316
|
+
function parseSettingsArg(spec, cwd2) {
|
|
152317
|
+
const s2 = String(spec).trim();
|
|
152318
|
+
let text = s2;
|
|
152319
|
+
if (!s2.startsWith("{")) {
|
|
152320
|
+
try {
|
|
152321
|
+
text = fs6.readFileSync(path7.resolve(cwd2, s2), "utf-8");
|
|
152322
|
+
} catch {
|
|
152323
|
+
return { ok: false, error: `--settings: cannot read "${s2}"` };
|
|
152324
|
+
}
|
|
152325
|
+
}
|
|
152326
|
+
let v;
|
|
152327
|
+
try {
|
|
152328
|
+
v = JSON.parse(text);
|
|
152329
|
+
} catch {
|
|
152330
|
+
return { ok: false, error: `--settings: ${s2.startsWith("{") ? "the inline value" : `"${s2}"`} is not valid JSON` };
|
|
152331
|
+
}
|
|
152332
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
152333
|
+
return { ok: false, error: "--settings: expected a JSON object" };
|
|
152334
|
+
return { ok: true, value: v };
|
|
152335
|
+
}
|
|
152336
|
+
function authStatusReport(a, env4) {
|
|
152337
|
+
if (!a || !a.token)
|
|
152338
|
+
return { loggedIn: false, source: null };
|
|
152339
|
+
const fromEnv = !!env4.NEXRALL_TOKEN && env4.NEXRALL_TOKEN.trim().length > 0;
|
|
152340
|
+
return { loggedIn: true, source: fromEnv ? "env" : "login", ...a.email ? { email: a.email } : {} };
|
|
152341
|
+
}
|
|
152342
|
+
function isFallbackWorthy(message) {
|
|
152343
|
+
const m2 = String(message);
|
|
152344
|
+
if (/\b(401|402|403|400|404|413|422)\b/.test(m2) && !/\b(429|5\d\d)\b/.test(m2))
|
|
152345
|
+
return false;
|
|
152346
|
+
return /\b(429|500|502|503|504|529)\b|overload|rate.?limit|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|socket hang up|fetch failed|stream (failed|stalled|ended)|timed? ?out/i.test(m2);
|
|
152347
|
+
}
|
|
152348
|
+
function parseFallbackModel(raw, primary) {
|
|
152349
|
+
const s2 = String(raw).trim();
|
|
152350
|
+
if (!s2)
|
|
152351
|
+
return { ok: false, error: "Invalid --fallback-model: it is empty" };
|
|
152352
|
+
if (s2 === primary)
|
|
152353
|
+
return { ok: false, error: `--fallback-model "${s2}" is the same as the main model; pick a different one` };
|
|
152354
|
+
return { ok: true, value: s2 };
|
|
152355
|
+
}
|
|
152356
|
+
function resolveSystemPrompt(f3) {
|
|
152357
|
+
if (f3.text !== void 0 && f3.file !== void 0) {
|
|
152358
|
+
return { ok: false, error: "--system-prompt and --system-prompt-file are mutually exclusive \u2014 pick one." };
|
|
152359
|
+
}
|
|
152360
|
+
if (f3.file !== void 0) {
|
|
152361
|
+
const r2 = readPromptFile(f3.file, f3.cwd, "--system-prompt-file");
|
|
152362
|
+
if (!r2.ok)
|
|
152363
|
+
return r2;
|
|
152364
|
+
if (!r2.value.trim())
|
|
152365
|
+
return { ok: false, error: `--system-prompt-file: ${f3.file} is empty.` };
|
|
152366
|
+
return { ok: true, value: r2.value };
|
|
152367
|
+
}
|
|
152368
|
+
if (f3.text !== void 0) {
|
|
152369
|
+
if (!f3.text.trim())
|
|
152370
|
+
return { ok: false, error: "--system-prompt: the value is empty." };
|
|
152371
|
+
return { ok: true, value: f3.text };
|
|
152372
|
+
}
|
|
152373
|
+
return { ok: true, value: void 0 };
|
|
152374
|
+
}
|
|
152375
|
+
function checkPartialMessages(f3) {
|
|
152376
|
+
if (!f3.includePartial)
|
|
152377
|
+
return null;
|
|
152378
|
+
if (f3.outputFormat !== "stream-json") {
|
|
152379
|
+
return "--include-partial-messages needs --output-format stream-json (it streams model deltas as they arrive).";
|
|
152380
|
+
}
|
|
152381
|
+
return null;
|
|
152382
|
+
}
|
|
152383
|
+
function checkReplayUserMessages(f3) {
|
|
152384
|
+
if (!f3.replay)
|
|
152385
|
+
return null;
|
|
152386
|
+
if (f3.inputFormat !== "stream-json") {
|
|
152387
|
+
return "--replay-user-messages needs --input-format stream-json (there is nothing to replay otherwise).";
|
|
152388
|
+
}
|
|
152389
|
+
if (f3.outputFormat !== "stream-json") {
|
|
152390
|
+
return "--replay-user-messages needs --output-format stream-json (the echo is written as an output event).";
|
|
152391
|
+
}
|
|
152392
|
+
return null;
|
|
152393
|
+
}
|
|
152394
|
+
function streamEventPayload(kind, chunk, extra = {}) {
|
|
152395
|
+
const delta = kind === "text" ? { type: "text_delta", text: chunk } : kind === "thinking" ? { type: "thinking_delta", thinking: chunk } : { type: "input_json_delta", partial_json: chunk };
|
|
152396
|
+
return { type: "stream_event", event: { type: "content_block_delta", delta }, ...extra };
|
|
152397
|
+
}
|
|
152398
|
+
function initEventPayload(i2) {
|
|
152399
|
+
return {
|
|
152400
|
+
type: "system",
|
|
152401
|
+
subtype: "init",
|
|
152402
|
+
session_id: i2.sessionId ?? "",
|
|
152403
|
+
cwd: i2.cwd,
|
|
152404
|
+
model: i2.model,
|
|
152405
|
+
tools: i2.tools,
|
|
152406
|
+
...i2.mcpServers ? { mcp_servers: i2.mcpServers } : {},
|
|
152407
|
+
...i2.permissionMode ? { permission_mode: i2.permissionMode } : {}
|
|
152408
|
+
};
|
|
152409
|
+
}
|
|
152410
|
+
function userReplayPayload(text) {
|
|
152411
|
+
return { type: "user", message: { role: "user", content: [{ type: "text", text }] } };
|
|
152412
|
+
}
|
|
152413
|
+
function parseAgentsArg(raw, cwd2) {
|
|
152414
|
+
if (raw === void 0)
|
|
152415
|
+
return { ok: true, value: [] };
|
|
152416
|
+
const text = String(raw).trim();
|
|
152417
|
+
if (!text)
|
|
152418
|
+
return { ok: false, error: "--agents: the value is empty." };
|
|
152419
|
+
let json = text;
|
|
152420
|
+
const looksLikePath = text.startsWith("@") || !text.startsWith("{") && fs6.existsSync(path7.resolve(cwd2, text));
|
|
152421
|
+
if (looksLikePath) {
|
|
152422
|
+
const file = text.startsWith("@") ? text.slice(1) : text;
|
|
152423
|
+
try {
|
|
152424
|
+
json = fs6.readFileSync(path7.resolve(cwd2, file), "utf-8");
|
|
152425
|
+
} catch (e2) {
|
|
152426
|
+
return { ok: false, error: `--agents: cannot read ${file}: ${e2.message}` };
|
|
152427
|
+
}
|
|
152428
|
+
}
|
|
152429
|
+
let obj2;
|
|
152430
|
+
try {
|
|
152431
|
+
obj2 = JSON.parse(json);
|
|
152432
|
+
} catch (e2) {
|
|
152433
|
+
return { ok: false, error: `--agents: not valid JSON: ${e2.message}` };
|
|
152434
|
+
}
|
|
152435
|
+
if (!obj2 || typeof obj2 !== "object" || Array.isArray(obj2)) {
|
|
152436
|
+
return { ok: false, error: '--agents: expected an object of { "name": { "description", "prompt" } }.' };
|
|
152437
|
+
}
|
|
152438
|
+
const out = [];
|
|
152439
|
+
for (const [name, v] of Object.entries(obj2)) {
|
|
152440
|
+
const a = v ?? {};
|
|
152441
|
+
if (!/^[A-Za-z0-9._-]+$/.test(name)) {
|
|
152442
|
+
return { ok: false, error: `--agents: "${name}" is not a usable agent name (letters, digits, . _ - only).` };
|
|
152443
|
+
}
|
|
152444
|
+
if (typeof a.description !== "string" || !a.description.trim()) {
|
|
152445
|
+
return { ok: false, error: `--agents: "${name}" needs a description (that is how the model decides to use it).` };
|
|
152446
|
+
}
|
|
152447
|
+
if (typeof a.prompt !== "string" || !a.prompt.trim()) {
|
|
152448
|
+
return { ok: false, error: `--agents: "${name}" needs a prompt (the agent's instructions).` };
|
|
152449
|
+
}
|
|
152450
|
+
const tools = Array.isArray(a.tools) ? a.tools.filter((t2) => typeof t2 === "string" && !!t2.trim()) : void 0;
|
|
152451
|
+
if (a.tools !== void 0 && (!tools || tools.length !== a.tools.length)) {
|
|
152452
|
+
return { ok: false, error: `--agents: "${name}".tools must be a list of tool names.` };
|
|
152453
|
+
}
|
|
152454
|
+
const model = typeof a.model === "string" && a.model.trim() ? a.model.trim() : void 0;
|
|
152455
|
+
out.push({ name, description: a.description.trim(), prompt: a.prompt, ...tools ? { tools } : {}, ...model ? { model } : {} });
|
|
152456
|
+
}
|
|
152457
|
+
if (!out.length)
|
|
152458
|
+
return { ok: false, error: "--agents: no agents defined." };
|
|
152459
|
+
return { ok: true, value: out };
|
|
152460
|
+
}
|
|
152079
152461
|
|
|
152080
152462
|
// src/commands/chat.ts
|
|
152081
152463
|
var import_code_core17 = __toESM(require_dist3(), 1);
|
|
@@ -167966,6 +168348,11 @@ function refreshEnvForTurn(env4, workDir) {
|
|
|
167966
168348
|
...collectGitEnv(workDir)
|
|
167967
168349
|
};
|
|
167968
168350
|
}
|
|
168351
|
+
var noDiscovery = false;
|
|
168352
|
+
var _cliAgentTypes = [];
|
|
168353
|
+
function setNoDiscovery(v) {
|
|
168354
|
+
noDiscovery = v;
|
|
168355
|
+
}
|
|
167969
168356
|
var NEXRALL_MD_CANDIDATES = [
|
|
167970
168357
|
"nexrall.md",
|
|
167971
168358
|
"NEXRALL.md",
|
|
@@ -167980,6 +168367,8 @@ var tryRead = (p) => {
|
|
|
167980
168367
|
}
|
|
167981
168368
|
};
|
|
167982
168369
|
function readNexrallMd(workDir) {
|
|
168370
|
+
if (noDiscovery)
|
|
168371
|
+
return void 0;
|
|
167983
168372
|
const parts = [];
|
|
167984
168373
|
const memContent = (0, import_code_core15.readAllMemory)(workDir);
|
|
167985
168374
|
if (memContent)
|
|
@@ -168228,6 +168617,11 @@ var _mcpManager = null;
|
|
|
168228
168617
|
var _mcpConfig = null;
|
|
168229
168618
|
var sessionTouched = [];
|
|
168230
168619
|
async function initMcp(workDir, override, strict) {
|
|
168620
|
+
if (noDiscovery && !override) {
|
|
168621
|
+
_mcpConfig = { mcpServers: {} };
|
|
168622
|
+
_mcpManager = null;
|
|
168623
|
+
return;
|
|
168624
|
+
}
|
|
168231
168625
|
_mcpConfig = strict && override ? { mcpServers: {} } : import_code_core15.McpManager.loadConfig(workDir);
|
|
168232
168626
|
if (override)
|
|
168233
168627
|
Object.assign(_mcpConfig.mcpServers, override.mcpServers);
|
|
@@ -168599,6 +168993,7 @@ function printErrorReport(err, aborted) {
|
|
|
168599
168993
|
for (const l2 of errorReportLines(err, aborted))
|
|
168600
168994
|
console.error(l2);
|
|
168601
168995
|
}
|
|
168996
|
+
var activeSessionIdGetter = () => void 0;
|
|
168602
168997
|
async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrallMd, mode, effort, checkpointManager, onProgress, takePendingInput, worktreeState, peerCtx) {
|
|
168603
168998
|
let lastUsage;
|
|
168604
168999
|
turnStats = freshTurnStats();
|
|
@@ -168633,6 +169028,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
|
|
|
168633
169028
|
spinner.start("thinking\u2026", { hint: false, verbs: true });
|
|
168634
169029
|
const result = await (0, import_code_core15.runAgentLoop)(messages, {
|
|
168635
169030
|
workDir,
|
|
169031
|
+
sessionId: activeSessionIdGetter(),
|
|
168636
169032
|
model: modelAlias,
|
|
168637
169033
|
env: refreshEnvForTurn(env4, workDir),
|
|
168638
169034
|
nexrallMd,
|
|
@@ -168665,6 +169061,8 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
|
|
|
168665
169061
|
onInjectedInput: () => {
|
|
168666
169062
|
},
|
|
168667
169063
|
backgroundAgents: cliBackgroundAgents,
|
|
169064
|
+
disableHooks: noDiscovery,
|
|
169065
|
+
_extraAgentTypes: _cliAgentTypes,
|
|
168668
169066
|
// The backend streams a cumulative output-token count (routes/code.js's
|
|
168669
169067
|
// sendProgress, throttled to ≤5/s) covering thinking, visible text AND
|
|
168670
169068
|
// tool-argument JSON. This used to be stored in a variable and rendered only
|
|
@@ -168958,13 +169356,27 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
168958
169356
|
let resultText = "";
|
|
168959
169357
|
let toolCallCount = 0;
|
|
168960
169358
|
let budgetHit = false;
|
|
169359
|
+
let turnsHit = false;
|
|
168961
169360
|
const emit = (obj2) => {
|
|
168962
169361
|
if (format === "stream-json")
|
|
168963
169362
|
process.stdout.write(JSON.stringify(obj2) + "\n");
|
|
168964
169363
|
};
|
|
169364
|
+
const verbose = (line) => {
|
|
169365
|
+
if (_headlessPolicy.verbose)
|
|
169366
|
+
process.stderr.write(line + "\n");
|
|
169367
|
+
};
|
|
169368
|
+
emit(initEventPayload({
|
|
169369
|
+
sessionId: activeSessionIdGetter(),
|
|
169370
|
+
cwd: workDir,
|
|
169371
|
+
model: modelAlias,
|
|
169372
|
+
tools: _headlessPolicy.onlyTools ? [..._headlessPolicy.onlyTools].sort() : (0, import_code_core15.knownToolNames)(),
|
|
169373
|
+
permissionMode: _headlessPolicy.permissionMode
|
|
169374
|
+
}));
|
|
168965
169375
|
emit({ type: "start", model: modelAlias, workDir });
|
|
169376
|
+
verbose(`[verbose] model=${modelAlias} cwd=${workDir} mode=${mode} session=${activeSessionIdGetter() ?? "-"}`);
|
|
168966
169377
|
const result = await (0, import_code_core15.runAgentLoop)(messages, {
|
|
168967
169378
|
workDir,
|
|
169379
|
+
sessionId: activeSessionIdGetter(),
|
|
168968
169380
|
model: modelAlias,
|
|
168969
169381
|
env: refreshEnvForTurn(env4, workDir),
|
|
168970
169382
|
nexrallMd,
|
|
@@ -168972,11 +169384,27 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
168972
169384
|
planMode: mode === "plan",
|
|
168973
169385
|
worktree: worktreeState,
|
|
168974
169386
|
backgroundAgents: cliBackgroundAgents,
|
|
169387
|
+
disableHooks: noDiscovery,
|
|
169388
|
+
_extraAgentTypes: _cliAgentTypes,
|
|
168975
169389
|
effort,
|
|
168976
169390
|
abortSignal: _abortSignal,
|
|
169391
|
+
// `--max-turns`: a hard round cap. autoContinue off, or the loop would extend it.
|
|
169392
|
+
..._headlessPolicy.maxTurns !== void 0 ? { maxIterations: _headlessPolicy.maxTurns, autoContinue: false } : {},
|
|
169393
|
+
// `--tools`: the allowlist is the same one sub-agents use, so removed tools are not even offered.
|
|
169394
|
+
..._headlessPolicy.onlyTools ? { _allowedTools: _headlessPolicy.onlyTools } : {},
|
|
169395
|
+
// Setting this callback makes the loop stop printing its own stop notice, so re-emit it —
|
|
169396
|
+
// otherwise every stop reason (stalled, no balance…) would go silent in headless runs.
|
|
169397
|
+
_onStopReason: (reason, notice) => {
|
|
169398
|
+
if (reason === "budget" && _headlessPolicy.maxTurns !== void 0)
|
|
169399
|
+
turnsHit = true;
|
|
169400
|
+
if (notice)
|
|
169401
|
+
emit({ type: "notice", text: notice });
|
|
169402
|
+
},
|
|
168977
169403
|
onText: (text) => {
|
|
168978
169404
|
resultText += text;
|
|
168979
169405
|
emit({ type: "text", text });
|
|
169406
|
+
if (_headlessPolicy.includePartial)
|
|
169407
|
+
emit(streamEventPayload("text", text));
|
|
168980
169408
|
},
|
|
168981
169409
|
// ── Reasoning-phase liveness ──────────────────────────────────────
|
|
168982
169410
|
// These used to be UNWIRED here while runTurn() (interactive) wired all
|
|
@@ -169007,6 +169435,8 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
169007
169435
|
// dropped on the floor for every headless run.
|
|
169008
169436
|
onThinking: (text) => {
|
|
169009
169437
|
emit({ type: "thinking", text });
|
|
169438
|
+
if (_headlessPolicy.includePartial)
|
|
169439
|
+
emit(streamEventPayload("thinking", text));
|
|
169010
169440
|
},
|
|
169011
169441
|
// onThinkingDelta is intentionally NOT wired. Its chunks concatenate to the
|
|
169012
169442
|
// same string `onThinking` emits in full above, so emitting both would
|
|
@@ -169023,6 +169453,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
169023
169453
|
onToolUse: (name, input, isSubTask, meta) => {
|
|
169024
169454
|
debugLog("tool_use", { tool: name, sub: !!isSubTask });
|
|
169025
169455
|
emit({ type: "tool_use", tool: name, input, ...toolIds(isSubTask, meta) });
|
|
169456
|
+
verbose(`[verbose] -> ${name}${isSubTask ? " (sub-agent)" : ""} ${toolTarget(name, input)}`);
|
|
169026
169457
|
},
|
|
169027
169458
|
// Live bash progress for scripted/CI consumers — lets a wrapper tail a
|
|
169028
169459
|
// long-running build/test command instead of blocking silently until
|
|
@@ -169030,12 +169461,15 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
169030
169461
|
// which is head/tail-capped), so a chatty command can emit many of these.
|
|
169031
169462
|
onToolStreamChunk: (name, chunk, isSubTask, meta) => {
|
|
169032
169463
|
emit({ type: "tool_stream", tool: name, chunk, ...toolIds(isSubTask, meta) });
|
|
169464
|
+
if (_headlessPolicy.includePartial)
|
|
169465
|
+
emit(streamEventPayload("tool", chunk, { tool: name, ...toolIds(isSubTask, meta) }));
|
|
169033
169466
|
},
|
|
169034
169467
|
onToolResult: (name, res, isSubTask, meta) => {
|
|
169035
169468
|
if (!isSubTask)
|
|
169036
169469
|
toolCallCount++;
|
|
169037
169470
|
debugLog("tool_result", { tool: name, ok: res.error === void 0, error: res.error, sub: !!isSubTask });
|
|
169038
169471
|
emit({ type: "tool_result", tool: name, ok: res.error === void 0, ...res.error ? { error: res.error } : {}, ...toolIds(isSubTask, meta) });
|
|
169472
|
+
verbose(`[verbose] <- ${name} ${res.error === void 0 ? "ok" : "FAILED: " + String(res.error).slice(0, 200)}`);
|
|
169039
169473
|
},
|
|
169040
169474
|
// Headless consumers get the partial report too — tagged, so a script can account
|
|
169041
169475
|
// for the real cost of a restarted turn — but it never becomes `lastUsage`, which
|
|
@@ -169076,6 +169510,10 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
169076
169510
|
// no human to confirm, they are denied unless NEXRALL_ALLOW_DESTRUCTIVE=1 is
|
|
169077
169511
|
// explicitly set for this CI run.
|
|
169078
169512
|
requestPermission: async ({ tool, input }) => {
|
|
169513
|
+
if (_headlessPolicy.onlyTools && !_headlessPolicy.onlyTools.has(tool) && !tool.startsWith("mcp__")) {
|
|
169514
|
+
emit({ type: "permission_denied", tool, reason: "tools" });
|
|
169515
|
+
return false;
|
|
169516
|
+
}
|
|
169079
169517
|
const d = (0, import_code_core15.isDestructiveBash)(tool, input);
|
|
169080
169518
|
const v = headlessVerdict({
|
|
169081
169519
|
rule: (0, import_code_core17.evaluatePermission)((0, import_code_core17.loadSettings)(workDir).permissions, tool, input, workDir),
|
|
@@ -169115,6 +169553,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
169115
169553
|
}
|
|
169116
169554
|
const extra = {
|
|
169117
169555
|
..._headlessPolicy.budgetUsd !== void 0 ? { cost_usd: _headlessPolicy.spent, ...budgetHit ? { stopped: "budget" } : {} } : {},
|
|
169556
|
+
...turnsHit ? { stopped: "max_turns", max_turns: _headlessPolicy.maxTurns } : {},
|
|
169118
169557
|
...structured ? { structured_output: structured.value } : {},
|
|
169119
169558
|
...schemaError ? { schema_error: schemaError } : {}
|
|
169120
169559
|
};
|
|
@@ -169137,6 +169576,8 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
|
|
|
169137
169576
|
}
|
|
169138
169577
|
if (budgetHit)
|
|
169139
169578
|
process.exitCode = 2;
|
|
169579
|
+
else if (turnsHit)
|
|
169580
|
+
process.exitCode = 4;
|
|
169140
169581
|
return { messages: result, usage: lastUsage };
|
|
169141
169582
|
}
|
|
169142
169583
|
function kbKey(action) {
|
|
@@ -169317,6 +169758,9 @@ function printSessions() {
|
|
|
169317
169758
|
console.log();
|
|
169318
169759
|
}
|
|
169319
169760
|
async function startChatSession(options) {
|
|
169761
|
+
if (options.bare)
|
|
169762
|
+
setNoDiscovery(true);
|
|
169763
|
+
_cliAgentTypes = (options.agents ?? []).map((a) => ({ ...a, source: "programmatic" }));
|
|
169320
169764
|
(0, import_code_core15.enableAgentTranscriptPersistence)();
|
|
169321
169765
|
const headless = options.outputFormat === "json" || options.outputFormat === "stream-json";
|
|
169322
169766
|
const requestedWorkDir = options.workDir;
|
|
@@ -169412,8 +169856,15 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169412
169856
|
});
|
|
169413
169857
|
}
|
|
169414
169858
|
const env4 = collectEnv(workDir);
|
|
169415
|
-
const
|
|
169416
|
-
|
|
169859
|
+
const briefBase = options.systemPrompt?.trim() ?? readNexrallMd(workDir);
|
|
169860
|
+
const agentPrefix = options.selectedAgent?.prompt?.trim();
|
|
169861
|
+
const nexrallMd = [agentPrefix, briefBase, options.appendSystemPrompt?.trim(), addDirNote(options.addDirs ?? []), options.jsonSchema ? schemaInstruction(options.jsonSchema) : ""].filter(Boolean).join("\n\n") || void 0;
|
|
169862
|
+
const agentTools = options.selectedAgent?.tools;
|
|
169863
|
+
const toolAllow = agentTools && options.onlyTools ? options.onlyTools.filter((t2) => agentTools.includes(t2)) : options.onlyTools ?? agentTools;
|
|
169864
|
+
_headlessPolicy = { strict: !!options.strictPermissions, budgetUsd: options.budgetUsd, spent: 0, schema: options.jsonSchema, maxTurns: options.maxTurns, onlyTools: toolAllow ? new Set(toolAllow) : void 0 };
|
|
169865
|
+
_headlessPolicy.verbose = !!options.verbose;
|
|
169866
|
+
_headlessPolicy.includePartial = !!options.includePartialMessages;
|
|
169867
|
+
_headlessPolicy.permissionMode = String(options.initialMode ?? "");
|
|
169417
169868
|
const saveSession2 = options.noSessionPersistence ? () => {
|
|
169418
169869
|
} : saveSession;
|
|
169419
169870
|
const interactive = !headless && !options.prompt && !options.stdinText;
|
|
@@ -169489,8 +169940,9 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169489
169940
|
));
|
|
169490
169941
|
}
|
|
169491
169942
|
let messages = [];
|
|
169492
|
-
let sessionId = crypto6.randomBytes(8).toString("hex");
|
|
169493
|
-
|
|
169943
|
+
let sessionId = options.newSessionId ?? crypto6.randomBytes(8).toString("hex");
|
|
169944
|
+
activeSessionIdGetter = () => sessionId;
|
|
169945
|
+
let sessionTitle = options.sessionName ?? "";
|
|
169494
169946
|
const modeLocked = options.lockMode === true;
|
|
169495
169947
|
let agentMode = modeLocked ? "plan" : options.initialMode ?? "auto";
|
|
169496
169948
|
const refuseIfLocked = (what) => {
|
|
@@ -169512,7 +169964,7 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169512
169964
|
for (const n of themeConfig.notes)
|
|
169513
169965
|
console.log(paint.dim(" theme: " + n));
|
|
169514
169966
|
}
|
|
169515
|
-
let skills = (0, import_code_core15.loadSkills)(workDir);
|
|
169967
|
+
let skills = noDiscovery ? [] : (0, import_code_core15.loadSkills)(workDir);
|
|
169516
169968
|
if (skills.length && !headless)
|
|
169517
169969
|
console.log(paint.dim(` ${skills.length} skill(s) loaded`));
|
|
169518
169970
|
let contextSet = loadContextSet(workDir);
|
|
@@ -169542,7 +169994,7 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169542
169994
|
process.exit(1);
|
|
169543
169995
|
}
|
|
169544
169996
|
if (options.debug) {
|
|
169545
|
-
const dbgPath = enableDebug();
|
|
169997
|
+
const dbgPath = enableDebug(options.debugFile);
|
|
169546
169998
|
if (dbgPath) {
|
|
169547
169999
|
debugLog("start", { version: CLI_VERSION, node: process.version, platform: process.platform, model: modelAlias, mode: agentMode, locked: modeLocked, headless, interactive });
|
|
169548
170000
|
if (!headless)
|
|
@@ -169555,7 +170007,7 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169555
170007
|
}
|
|
169556
170008
|
}
|
|
169557
170009
|
if (options.resume || options.continueLast) {
|
|
169558
|
-
const stored = options.continueLast ? lastSessionForDir(workDir) : options.resume === "last" ? lastSession() : loadSession(options.resume);
|
|
170010
|
+
const stored = options.continueLast ? lastSessionForDir(workDir) : options.resume === "last" ? lastSession() : loadSession(options.resume) ?? matchSessionByName(listSessions(), options.resume);
|
|
169559
170011
|
if (!stored) {
|
|
169560
170012
|
if (options.continueLast) {
|
|
169561
170013
|
console.error(paint.red(" No conversation to continue in this folder."));
|
|
@@ -169569,7 +170021,13 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169569
170021
|
messages = stored.messages;
|
|
169570
170022
|
sessionId = stored.id;
|
|
169571
170023
|
sessionTitle = stored.title;
|
|
169572
|
-
|
|
170024
|
+
if (options.forkSession) {
|
|
170025
|
+
sessionId = options.newSessionId ?? crypto6.randomBytes(8).toString("hex");
|
|
170026
|
+
sessionTitle = options.sessionName ?? forkTitle(stored.title, "");
|
|
170027
|
+
} else if (options.sessionName) {
|
|
170028
|
+
sessionTitle = options.sessionName;
|
|
170029
|
+
}
|
|
170030
|
+
startSessionLog(sessionId, true, options.forkSession ? { workDir, forkOf: stored.id } : { workDir });
|
|
169573
170031
|
try {
|
|
169574
170032
|
const compacted = await (0, import_code_core15.compactMessagesForResume)(messages, {
|
|
169575
170033
|
workDir,
|
|
@@ -169643,6 +170101,9 @@ Resume it with: nex --worktree ${worktreeState.name}
|
|
|
169643
170101
|
${text}` : "");
|
|
169644
170102
|
}
|
|
169645
170103
|
checkpoints.beginTurn(text, messages.length);
|
|
170104
|
+
if (options.replayUserMessages && options.inputFormatWasStreamJson && options.outputFormat === "stream-json") {
|
|
170105
|
+
process.stdout.write(JSON.stringify(userReplayPayload(text)) + "\n");
|
|
170106
|
+
}
|
|
169646
170107
|
messages.push({ role: "user", content: [{ type: "text", text }] });
|
|
169647
170108
|
const abortSignal2 = { aborted: false };
|
|
169648
170109
|
const onSigint = () => {
|
|
@@ -169651,8 +170112,31 @@ ${text}` : "");
|
|
|
169651
170112
|
process.once("SIGINT", onSigint);
|
|
169652
170113
|
try {
|
|
169653
170114
|
const fmt = options.outputFormat;
|
|
170115
|
+
const replay = (text2) => {
|
|
170116
|
+
if (options.replayUserMessages && fmt === "stream-json")
|
|
170117
|
+
process.stdout.write(JSON.stringify(userReplayPayload(text2)) + "\n");
|
|
170118
|
+
};
|
|
169654
170119
|
const runOnce = () => fmt === "json" || fmt === "stream-json" ? runTurnHeadless(messages, modelAlias, workDir, abortSignal2, env4, nexrallMd, agentMode, effortLevel, fmt, checkpoints, saveProgress, worktreeState) : runTurn(messages, modelAlias, workDir, abortSignal2, env4, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress, void 0, worktreeState, peerCtx);
|
|
169655
|
-
|
|
170120
|
+
const runWithFallback = async () => {
|
|
170121
|
+
try {
|
|
170122
|
+
return await runOnce();
|
|
170123
|
+
} catch (e2) {
|
|
170124
|
+
if (!options.fallbackModel || modelAlias === options.fallbackModel || abortSignal2.aborted || !isFallbackWorthy(String(e2.message)))
|
|
170125
|
+
throw e2;
|
|
170126
|
+
const from = modelAlias;
|
|
170127
|
+
modelAlias = options.fallbackModel;
|
|
170128
|
+
const salvagedMsgs = (0, import_code_core15.salvageHistory)(e2);
|
|
170129
|
+
if (salvagedMsgs)
|
|
170130
|
+
messages = salvagedMsgs;
|
|
170131
|
+
const note = `Model ${from} failed (${String(e2.message).slice(0, 120)}); retrying on ${modelAlias}`;
|
|
170132
|
+
if (fmt === "stream-json")
|
|
170133
|
+
process.stdout.write(JSON.stringify({ type: "notice", text: note }) + "\n");
|
|
170134
|
+
else
|
|
170135
|
+
console.error(note);
|
|
170136
|
+
return await runOnce();
|
|
170137
|
+
}
|
|
170138
|
+
};
|
|
170139
|
+
let result = await runWithFallback();
|
|
169656
170140
|
messages = result.messages;
|
|
169657
170141
|
while (!abortSignal2.aborted && (cliBackgroundAgents.runningCount() > 0 || cliBackgroundAgents.hasPending())) {
|
|
169658
170142
|
if (!cliBackgroundAgents.hasPending()) {
|
|
@@ -169669,6 +170153,7 @@ ${text}` : "");
|
|
|
169669
170153
|
break;
|
|
169670
170154
|
commitCheckpoint();
|
|
169671
170155
|
checkpoints.beginTurn(follow, messages.length);
|
|
170156
|
+
replay(follow);
|
|
169672
170157
|
messages.push({ role: "user", content: [{ type: "text", text: follow }] });
|
|
169673
170158
|
result = await runOnce();
|
|
169674
170159
|
messages = result.messages;
|
|
@@ -170576,6 +171061,7 @@ ${summaryText}` }] },
|
|
|
170576
171061
|
saveSession2(sessionId, sessionTitle || "Untitled", workDir, messages);
|
|
170577
171062
|
console.log(paint.green(` Compacted ${toSummarize.length} messages \u2192 ${messages.length} messages.`));
|
|
170578
171063
|
emitEvent("context.compacted", { where: "manual", before: toSummarize.length + KEEP, after: messages.length });
|
|
171064
|
+
await (0, import_code_core16.fireManualPostCompactHook)(workDir, sessionId, toSummarize.length);
|
|
170579
171065
|
if (pin.attachments.length)
|
|
170580
171066
|
console.log(paint.dim(` ${pin.attachments.length} pinned file(s) re-attached.`));
|
|
170581
171067
|
for (const n of pin.notes)
|
|
@@ -172916,7 +173402,17 @@ var require3 = createRequire2(import.meta.url);
|
|
|
172916
173402
|
var NEX_VERSION = require3("../package.json").version;
|
|
172917
173403
|
var program2 = new Command();
|
|
172918
173404
|
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version(NEX_VERSION).enablePositionalOptions();
|
|
172919
|
-
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
173405
|
+
var auth = program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
173406
|
+
auth.command("status").description("Show whether you are logged in (exit 0 = yes, 1 = no). --json for scripts; the token is never printed").option("--json", "Machine-readable output").action((opts) => {
|
|
173407
|
+
const r2 = authStatusReport((0, import_code_core21.loadAuth)(), process.env);
|
|
173408
|
+
if (opts.json)
|
|
173409
|
+
console.log(JSON.stringify(r2));
|
|
173410
|
+
else if (!r2.loggedIn)
|
|
173411
|
+
console.log("Not logged in. Run: nex auth");
|
|
173412
|
+
else
|
|
173413
|
+
console.log(`Logged in${r2.email ? ` as ${r2.email}` : ""} (${r2.source === "env" ? "NEXRALL_TOKEN" : "saved login"})`);
|
|
173414
|
+
process.exit(r2.loggedIn ? 0 : 1);
|
|
173415
|
+
});
|
|
172920
173416
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
172921
173417
|
program2.command("doctor").description("Check Node, sign-in, network, terminal, MCP and storage, with a fix for each problem").option("-d, --dir <path>", "Project folder to check (default: current directory)").action(async (opts) => {
|
|
172922
173418
|
const workDir = opts.dir ? path26.resolve(opts.dir) : process.cwd();
|
|
@@ -173115,7 +173611,7 @@ program2.command("sessions").description("List, export, import, or delete saved
|
|
|
173115
173611
|
console.log(paint.dim(" nex sessions --events <id> [--json] to follow its event stream"));
|
|
173116
173612
|
console.log();
|
|
173117
173613
|
});
|
|
173118
|
-
program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, --turbo", "Use Claude Sonnet 5.5 (fast)").option("-u, --ultra", "Use Claude Fable 5.1 (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-5 | gpt-5.4 | gpt-4.1").option("-r, --resume [id]", "Resume last session, or a specific session id").option("-c, --continue", "Continue the latest conversation in this folder").option("--plan", "Start in plan mode (read-only; /mode or Shift+Tab can leave it)").option("--readonly", "Read-only for the whole session: plan mode that cannot be switched off").option("--auto", "Start in auto mode (the default \u2014 edits and commands run without asking)").option("--mode <mode>", "Starting permission mode: ask | edit | auto | plan").option("--fullscreen", "Draw the whole session on the terminal's alternate screen: the transcript re-wraps on resize and PgUp/PgDn page through it (no terminal scrollback while it is on; /fullscreen toggles it)").option("--bg", "Run the prompt in the background (headless); returns a job id. See: nex jobs | logs | attach | kill").option("--debug", "Write request timing, retries and errors to a debug log file").option("--no-color", "Disable colored output (also honours NO_COLOR)").option("-w, --worktree [name]", "Isolate this session in its own worktree \u2014 a separate directory (and, in a git repo, its own branch) so it can never overwrite files another session (or you) are touching in the main checkout. Omit a name for an auto-generated one; pass an existing name to resume that same worktree.").option("-e, --effort <level>", "Thinking effort \u2014 real levels depend on the model (e.g. DeepSeek: low|high|max, Qwen: low|high); run `nex --model <id>` then `/effort` with no argument to see a model's scale").option("--no-banner", "Skip the ASCII banner (useful in scripts/pipes)").option("--output-format <fmt>", "One-shot output format: text | json | stream-json (implies auto-approve)").option("--audit [path]", "Log every tool call (and refusal) to a JSONL trail (default: .nexrall/audit/<session>.jsonl)").option("--permission-mode <mode>", "Permission mode: ask | edit | auto | plan (also Claude Code names: default | acceptEdits | bypassPermissions). With --output-format, anything that would prompt is DENIED instead of auto-approved").option("--allowedTools, --allowed-tools <tools...>", 'Tools/rules to allow without asking, e.g. "Bash(git *)" Edit (comma or space separated; put the prompt BEFORE this flag)').option("--disallowedTools, --disallowed-tools <tools...>", 'Tools/rules to deny, e.g. "Bash(rm *)" (deny always wins)').option("--append-system-prompt <text>", "Extra instructions appended to the project brief for this run").option("--max-budget-usd <amount>", "Stop a one-shot run once it has spent this many dollars (needs --output-format)").option("--mcp-config <configs...>", 'Load MCP servers from JSON files or inline JSON ({"mcpServers":{\u2026}}) for this run').option("--strict-mcp-config", "With --mcp-config: use ONLY those servers, ignoring ~/.nexrall/mcp.json and the project one").option("--add-dir <dirs...>", "Extra directories the agent may read and edit besides the project folder").option("--input-format <format>", "text (default) | stream-json \u2014 read the prompt as Claude-Code-style NDJSON user messages on stdin").option("--json-schema <schema>", 'JSON Schema (inline or file) the final answer must satisfy; needs --output-format. Result gains "structured_output"; exit 3 if it does not validate').option("--no-session-persistence", "Do not save this run to disk (needs --output-format; it cannot be resumed)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
|
|
173614
|
+
program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, --turbo", "Use Claude Sonnet 5.5 (fast)").option("-u, --ultra", "Use Claude Fable 5.1 (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-5 | gpt-5.4 | gpt-4.1").option("-r, --resume [id]", "Resume last session, or a specific session id").option("-c, --continue", "Continue the latest conversation in this folder").option("--plan", "Start in plan mode (read-only; /mode or Shift+Tab can leave it)").option("--readonly", "Read-only for the whole session: plan mode that cannot be switched off").option("--auto", "Start in auto mode (the default \u2014 edits and commands run without asking)").option("--mode <mode>", "Starting permission mode: ask | edit | auto | plan").option("--fullscreen", "Draw the whole session on the terminal's alternate screen: the transcript re-wraps on resize and PgUp/PgDn page through it (no terminal scrollback while it is on; /fullscreen toggles it)").option("--bg", "Run the prompt in the background (headless); returns a job id. See: nex jobs | logs | attach | kill").option("--debug", "Write request timing, retries and errors to a debug log file").option("--no-color", "Disable colored output (also honours NO_COLOR)").option("-w, --worktree [name]", "Isolate this session in its own worktree \u2014 a separate directory (and, in a git repo, its own branch) so it can never overwrite files another session (or you) are touching in the main checkout. Omit a name for an auto-generated one; pass an existing name to resume that same worktree.").option("-e, --effort <level>", "Thinking effort \u2014 real levels depend on the model (e.g. DeepSeek: low|high|max, Qwen: low|high); run `nex --model <id>` then `/effort` with no argument to see a model's scale").option("--no-banner", "Skip the ASCII banner (useful in scripts/pipes)").option("--output-format <fmt>", "One-shot output format: text | json | stream-json (implies auto-approve)").option("--audit [path]", "Log every tool call (and refusal) to a JSONL trail (default: .nexrall/audit/<session>.jsonl)").option("--permission-mode <mode>", "Permission mode: ask | edit | auto | plan (also Claude Code names: default | acceptEdits | bypassPermissions). With --output-format, anything that would prompt is DENIED instead of auto-approved").option("--allowedTools, --allowed-tools <tools...>", 'Tools/rules to allow without asking, e.g. "Bash(git *)" Edit (comma or space separated; put the prompt BEFORE this flag)').option("--disallowedTools, --disallowed-tools <tools...>", 'Tools/rules to deny, e.g. "Bash(rm *)" (deny always wins)').option("--append-system-prompt <text>", "Extra instructions appended to the project brief for this run").option("--max-budget-usd <amount>", "Stop a one-shot run once it has spent this many dollars (needs --output-format)").option("--mcp-config <configs...>", 'Load MCP servers from JSON files or inline JSON ({"mcpServers":{\u2026}}) for this run').option("--strict-mcp-config", "With --mcp-config: use ONLY those servers, ignoring ~/.nexrall/mcp.json and the project one").option("--add-dir <dirs...>", "Extra directories the agent may read and edit besides the project folder").option("--input-format <format>", "text (default) | stream-json \u2014 read the prompt as Claude-Code-style NDJSON user messages on stdin").option("--json-schema <schema>", 'JSON Schema (inline or file) the final answer must satisfy; needs --output-format. Result gains "structured_output"; exit 3 if it does not validate').option("--no-session-persistence", "Do not save this run to disk (needs --output-format; it cannot be resumed)").option("--max-turns <n>", "Stop a headless run after N agent rounds (needs --output-format; exit 4 when it hits the limit)").option("--append-system-prompt-file <path>", "Like --append-system-prompt, read from a file").option("--system-prompt <text>", "REPLACE the project brief (nexrall.md + memory + global instructions) with this text for this run").option("--system-prompt-file <path>", "Like --system-prompt, read from a file").option("--verbose", "Print progress detail (tool calls, timings, usage) to stderr while a one-shot run works").option("--include-partial-messages", "Stream model deltas as they arrive (needs --output-format stream-json)").option("--replay-user-messages", "Echo each stdin message back on stdout as it is consumed (needs --input-format stream-json --output-format stream-json)").option("--agents <json-or-file>", 'Define extra sub-agents for this run: a JSON object { "name": { "description", "prompt", "tools", "model" } } or @file.json').option("--agent <name>", "Run this session AS that agent (its prompt, tools and model), from .nexrall/agents, a plugin, --agents or a builtin").option("--bare", "Start without discovery: no nexrall.md/memory, skills, plugins, hooks or MCP config files").option("--safe-mode", "Same as --bare (Claude Code's troubleshooting name)").option("--tools <list>", 'Exactly these built-in tools ("Bash,Read,Edit", "default", or "" for none); the rest are removed, not just unapproved').option("--session-id <id>", "Use this id for a NEW conversation (letters, digits, - _), so a script can find it again").option("-n, --name <name>", "Name the conversation; `nex -r <name>` resumes it by that name").option("--fork-session", "With --resume/--continue: branch into a new session instead of continuing the original").option("--fallback-model <name>", "If the main model is overloaded or unreachable, retry the turn once on this model (needs --output-format)").option("--settings <file|json>", "Settings (permissions, hooks\u2026) for this run only: a JSON file or an inline JSON object; merged over the user/project settings").option("--debug-file <path>", "Write the --debug diagnostics log to this file (implies --debug)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
|
|
173119
173615
|
if (!(0, import_code_core20.isAuthenticated)()) {
|
|
173120
173616
|
console.error("Not logged in. Run: nex auth");
|
|
173121
173617
|
process.exit(1);
|
|
@@ -173147,8 +173643,43 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
|
|
|
173147
173643
|
console.error(sessErr);
|
|
173148
173644
|
process.exit(1);
|
|
173149
173645
|
}
|
|
173646
|
+
let newSessionId;
|
|
173647
|
+
let sessionName;
|
|
173648
|
+
if (options.sessionId !== void 0) {
|
|
173649
|
+
const r2 = parseSessionId(options.sessionId);
|
|
173650
|
+
if (!r2.ok) {
|
|
173651
|
+
console.error(r2.error);
|
|
173652
|
+
process.exit(1);
|
|
173653
|
+
}
|
|
173654
|
+
newSessionId = r2.value;
|
|
173655
|
+
}
|
|
173656
|
+
if (options.name !== void 0) {
|
|
173657
|
+
const r2 = parseSessionName(options.name);
|
|
173658
|
+
if (!r2.ok) {
|
|
173659
|
+
console.error(r2.error);
|
|
173660
|
+
process.exit(1);
|
|
173661
|
+
}
|
|
173662
|
+
sessionName = r2.value;
|
|
173663
|
+
}
|
|
173664
|
+
const identErr = checkSessionIdentityFlags({ sessionId: newSessionId, resume: options.resume, continue: options.continue, fork: options.forkSession });
|
|
173665
|
+
if (identErr) {
|
|
173666
|
+
console.error(identErr);
|
|
173667
|
+
process.exit(1);
|
|
173668
|
+
}
|
|
173669
|
+
if (newSessionId && loadSession(newSessionId) && !options.forkSession) {
|
|
173670
|
+
console.error(`--session-id "${newSessionId}" already exists. Resume it with: nex --resume ${newSessionId}`);
|
|
173671
|
+
process.exit(1);
|
|
173672
|
+
}
|
|
173150
173673
|
if (options.yolo || permBypass)
|
|
173151
173674
|
setAutoApprove("all");
|
|
173675
|
+
if (options.settings !== void 0) {
|
|
173676
|
+
const st = parseSettingsArg(options.settings, process.cwd());
|
|
173677
|
+
if (!st.ok) {
|
|
173678
|
+
console.error(st.error);
|
|
173679
|
+
process.exit(1);
|
|
173680
|
+
}
|
|
173681
|
+
(0, import_code_core21.setSessionSettings)(st.value);
|
|
173682
|
+
}
|
|
173152
173683
|
const allowRules = parseToolList(options.allowedTools);
|
|
173153
173684
|
const denyRules = parseToolList(options.disallowedTools);
|
|
173154
173685
|
(0, import_code_core21.setSessionRules)({ allow: allowRules, deny: denyRules });
|
|
@@ -173163,11 +173694,83 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
|
|
|
173163
173694
|
}
|
|
173164
173695
|
if (options.sessionPersistence === false)
|
|
173165
173696
|
process.env.NEXRALL_EVENTS = "off";
|
|
173166
|
-
|
|
173697
|
+
let maxTurns;
|
|
173698
|
+
if (options.maxTurns !== void 0) {
|
|
173699
|
+
const t2 = parseMaxTurns(options.maxTurns);
|
|
173700
|
+
if (!t2.ok) {
|
|
173701
|
+
console.error(t2.error);
|
|
173702
|
+
process.exit(1);
|
|
173703
|
+
}
|
|
173704
|
+
maxTurns = t2.value;
|
|
173705
|
+
}
|
|
173706
|
+
const headlessErr = checkHeadlessOnly({ outputFormat: options.outputFormat, budget: budgetUsd, noSessionPersistence: options.sessionPersistence === false, maxTurns });
|
|
173167
173707
|
if (headlessErr) {
|
|
173168
173708
|
console.error(headlessErr);
|
|
173169
173709
|
process.exit(1);
|
|
173170
173710
|
}
|
|
173711
|
+
const agentsParsed = parseAgentsArg(options.agents, process.cwd());
|
|
173712
|
+
if (!agentsParsed.ok) {
|
|
173713
|
+
console.error(agentsParsed.error);
|
|
173714
|
+
process.exit(1);
|
|
173715
|
+
}
|
|
173716
|
+
const extraTypes = agentsParsed.value.map((a) => ({
|
|
173717
|
+
name: a.name,
|
|
173718
|
+
description: a.description,
|
|
173719
|
+
prompt: a.prompt,
|
|
173720
|
+
source: "programmatic",
|
|
173721
|
+
...a.tools ? { tools: a.tools } : {},
|
|
173722
|
+
...a.model ? { model: a.model } : {}
|
|
173723
|
+
}));
|
|
173724
|
+
if (options.agent && options.bare) {
|
|
173725
|
+
console.error("--agent needs agent discovery, which --bare/--safe-mode turns off \u2014 drop one of them.");
|
|
173726
|
+
process.exit(1);
|
|
173727
|
+
}
|
|
173728
|
+
let selectedAgent;
|
|
173729
|
+
if (options.agent) {
|
|
173730
|
+
const catalogue = (0, import_code_core21.loadAgentTypesWithWarnings)(process.cwd(), extraTypes).types;
|
|
173731
|
+
selectedAgent = (0, import_code_core21.findAgentType)(catalogue, String(options.agent));
|
|
173732
|
+
if (!selectedAgent) {
|
|
173733
|
+
console.error(`Unknown agent "${options.agent}". Available: ${catalogue.map((t2) => t2.name).join(", ")}`);
|
|
173734
|
+
process.exit(1);
|
|
173735
|
+
}
|
|
173736
|
+
}
|
|
173737
|
+
const sysPrompt = resolveSystemPrompt({ text: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0, file: options.systemPromptFile, cwd: process.cwd() });
|
|
173738
|
+
if (!sysPrompt.ok) {
|
|
173739
|
+
console.error(sysPrompt.error);
|
|
173740
|
+
process.exit(1);
|
|
173741
|
+
}
|
|
173742
|
+
let appendSystemPrompt = options.appendSystemPrompt;
|
|
173743
|
+
if (options.appendSystemPromptFile !== void 0) {
|
|
173744
|
+
const f3 = readPromptFile(options.appendSystemPromptFile, process.cwd());
|
|
173745
|
+
if (!f3.ok) {
|
|
173746
|
+
console.error(f3.error);
|
|
173747
|
+
process.exit(1);
|
|
173748
|
+
}
|
|
173749
|
+
appendSystemPrompt = [appendSystemPrompt, f3.value].filter(Boolean).join("\n\n");
|
|
173750
|
+
}
|
|
173751
|
+
let onlyTools;
|
|
173752
|
+
if (options.tools !== void 0) {
|
|
173753
|
+
const t2 = parseToolsFlag(options.tools === true ? "" : options.tools, (0, import_code_core21.knownToolNames)());
|
|
173754
|
+
if (!t2.ok) {
|
|
173755
|
+
console.error(t2.error);
|
|
173756
|
+
process.exit(1);
|
|
173757
|
+
}
|
|
173758
|
+
onlyTools = t2.value;
|
|
173759
|
+
}
|
|
173760
|
+
let fallbackModel;
|
|
173761
|
+
if (options.fallbackModel !== void 0) {
|
|
173762
|
+
if (!options.outputFormat) {
|
|
173763
|
+
console.error("--fallback-model only works with --output-format (a one-shot, non-interactive run).");
|
|
173764
|
+
process.exit(1);
|
|
173765
|
+
}
|
|
173766
|
+
const f3 = parseFallbackModel(options.fallbackModel, model);
|
|
173767
|
+
if (!f3.ok) {
|
|
173768
|
+
console.error(f3.error);
|
|
173769
|
+
process.exit(1);
|
|
173770
|
+
}
|
|
173771
|
+
fallbackModel = f3.value;
|
|
173772
|
+
}
|
|
173773
|
+
const debugFile = options.debugFile !== void 0 ? path26.resolve(String(options.debugFile)) : void 0;
|
|
173171
173774
|
let mcpOverride;
|
|
173172
173775
|
if (options.mcpConfig) {
|
|
173173
173776
|
const m2 = parseMcpConfigs(options.mcpConfig, process.cwd());
|
|
@@ -173260,6 +173863,16 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
|
|
|
173260
173863
|
}
|
|
173261
173864
|
outputFormat = fmt;
|
|
173262
173865
|
}
|
|
173866
|
+
const partialErr = checkPartialMessages({ includePartial: !!options.includePartialMessages, outputFormat });
|
|
173867
|
+
if (partialErr) {
|
|
173868
|
+
console.error(partialErr);
|
|
173869
|
+
process.exit(1);
|
|
173870
|
+
}
|
|
173871
|
+
const replayErr = checkReplayUserMessages({ replay: !!options.replayUserMessages, inputFormat: options.inputFormat, outputFormat });
|
|
173872
|
+
if (replayErr) {
|
|
173873
|
+
console.error(replayErr);
|
|
173874
|
+
process.exit(1);
|
|
173875
|
+
}
|
|
173263
173876
|
const fsErr = checkFullscreenFlag({
|
|
173264
173877
|
fullscreen: !!options.fullscreen,
|
|
173265
173878
|
isTTY: !!process.stdout.isTTY,
|
|
@@ -173279,7 +173892,7 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
|
|
|
173279
173892
|
}
|
|
173280
173893
|
effort = lvl;
|
|
173281
173894
|
}
|
|
173282
|
-
await startChatSession({ model, workDir, prompt: prompt2, resume, continueLast: !!options.continue, initialMode: modeRes.mode, lockMode: modeRes.locked, debug: !!options.debug, stdinText, stdinFollowUps, showBanner, outputFormat, effort, audit: options.audit, worktree, fullscreen: !!options.fullscreen, strictPermissions: options.permissionMode !== void 0, budgetUsd, appendSystemPrompt: options.
|
|
173895
|
+
await startChatSession({ model, workDir, prompt: prompt2, resume, continueLast: !!options.continue, initialMode: modeRes.mode, lockMode: modeRes.locked, debug: !!options.debug || debugFile !== void 0, debugFile, fallbackModel, maxTurns, onlyTools, newSessionId, sessionName, forkSession: !!options.forkSession, stdinText, stdinFollowUps, showBanner, outputFormat, effort, audit: options.audit, worktree, fullscreen: !!options.fullscreen, strictPermissions: options.permissionMode !== void 0, budgetUsd, appendSystemPrompt, mcpOverride, strictMcp: !!options.strictMcpConfig, systemPrompt: sysPrompt.value, verbose: !!options.verbose, includePartialMessages: !!options.includePartialMessages, agents: agentsParsed.value, selectedAgent: selectedAgent ? { name: selectedAgent.name, prompt: selectedAgent.prompt, tools: selectedAgent.tools, model: selectedAgent.model } : void 0, replayUserMessages: !!options.replayUserMessages, inputFormatWasStreamJson: options.inputFormat !== void 0 && String(options.inputFormat).toLowerCase() === "stream-json", bare: !!(options.bare || options.safeMode), noSessionPersistence: options.sessionPersistence === false, addDirs: addDirs.value, jsonSchema });
|
|
173283
173896
|
});
|
|
173284
173897
|
program2.parse();
|
|
173285
173898
|
/*! Bundled license information:
|