nexrall-code 0.5.202 → 0.5.204
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 +375 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3364,6 +3364,109 @@ var require_auth = __commonJS({
|
|
|
3364
3364
|
}
|
|
3365
3365
|
});
|
|
3366
3366
|
|
|
3367
|
+
// ../core/dist/auth/device.js
|
|
3368
|
+
var require_device = __commonJS({
|
|
3369
|
+
"../core/dist/auth/device.js"(exports) {
|
|
3370
|
+
"use strict";
|
|
3371
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
3372
|
+
if (k2 === void 0)
|
|
3373
|
+
k2 = k;
|
|
3374
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
3375
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
3376
|
+
desc = { enumerable: true, get: function() {
|
|
3377
|
+
return m2[k];
|
|
3378
|
+
} };
|
|
3379
|
+
}
|
|
3380
|
+
Object.defineProperty(o, k2, desc);
|
|
3381
|
+
} : function(o, m2, k, k2) {
|
|
3382
|
+
if (k2 === void 0)
|
|
3383
|
+
k2 = k;
|
|
3384
|
+
o[k2] = m2[k];
|
|
3385
|
+
});
|
|
3386
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
3387
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
3388
|
+
} : function(o, v) {
|
|
3389
|
+
o["default"] = v;
|
|
3390
|
+
});
|
|
3391
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
3392
|
+
var ownKeys = function(o) {
|
|
3393
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
3394
|
+
var ar = [];
|
|
3395
|
+
for (var k in o2)
|
|
3396
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
3397
|
+
ar[ar.length] = k;
|
|
3398
|
+
return ar;
|
|
3399
|
+
};
|
|
3400
|
+
return ownKeys(o);
|
|
3401
|
+
};
|
|
3402
|
+
return function(mod) {
|
|
3403
|
+
if (mod && mod.__esModule)
|
|
3404
|
+
return mod;
|
|
3405
|
+
var result = {};
|
|
3406
|
+
if (mod != null) {
|
|
3407
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
3408
|
+
if (k[i2] !== "default")
|
|
3409
|
+
__createBinding(result, mod, k[i2]);
|
|
3410
|
+
}
|
|
3411
|
+
__setModuleDefault(result, mod);
|
|
3412
|
+
return result;
|
|
3413
|
+
};
|
|
3414
|
+
}();
|
|
3415
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3416
|
+
exports.setDeviceIdSource = setDeviceIdSource;
|
|
3417
|
+
exports.getDeviceId = getDeviceId;
|
|
3418
|
+
exports._resetDeviceIdForTests = _resetDeviceIdForTests;
|
|
3419
|
+
var crypto6 = __importStar(__require("crypto"));
|
|
3420
|
+
var fs33 = __importStar(__require("fs"));
|
|
3421
|
+
var os25 = __importStar(__require("os"));
|
|
3422
|
+
var path36 = __importStar(__require("path"));
|
|
3423
|
+
var DEVICE_DIR = path36.join(os25.homedir(), ".nexrall");
|
|
3424
|
+
var DEVICE_FILE = path36.join(DEVICE_DIR, "device-id");
|
|
3425
|
+
var injectedSource = null;
|
|
3426
|
+
var cached = null;
|
|
3427
|
+
function setDeviceIdSource(value) {
|
|
3428
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
3429
|
+
injectedSource = trimmed || null;
|
|
3430
|
+
cached = null;
|
|
3431
|
+
}
|
|
3432
|
+
function readOrCreateMachineValue() {
|
|
3433
|
+
const env3 = process.env.NEXRALL_DEVICE_ID;
|
|
3434
|
+
if (env3 && env3.trim())
|
|
3435
|
+
return env3.trim();
|
|
3436
|
+
try {
|
|
3437
|
+
if (fs33.existsSync(DEVICE_FILE)) {
|
|
3438
|
+
const existing = fs33.readFileSync(DEVICE_FILE, "utf-8").trim();
|
|
3439
|
+
if (existing)
|
|
3440
|
+
return existing;
|
|
3441
|
+
}
|
|
3442
|
+
const fresh = crypto6.randomBytes(24).toString("hex");
|
|
3443
|
+
fs33.mkdirSync(DEVICE_DIR, { recursive: true });
|
|
3444
|
+
fs33.writeFileSync(DEVICE_FILE, fresh + "\n", { mode: 384 });
|
|
3445
|
+
return fresh;
|
|
3446
|
+
} catch {
|
|
3447
|
+
return null;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
function getDeviceId() {
|
|
3451
|
+
if (cached)
|
|
3452
|
+
return cached;
|
|
3453
|
+
try {
|
|
3454
|
+
const source2 = injectedSource ?? readOrCreateMachineValue();
|
|
3455
|
+
if (!source2)
|
|
3456
|
+
return null;
|
|
3457
|
+
cached = crypto6.createHash("sha256").update(source2).digest("hex");
|
|
3458
|
+
return cached;
|
|
3459
|
+
} catch {
|
|
3460
|
+
return null;
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
function _resetDeviceIdForTests() {
|
|
3464
|
+
injectedSource = null;
|
|
3465
|
+
cached = null;
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
});
|
|
3469
|
+
|
|
3367
3470
|
// ../../node_modules/.pnpm/eventsource-parser@1.1.2/node_modules/eventsource-parser/dist/index.cjs
|
|
3368
3471
|
var require_dist = __commonJS({
|
|
3369
3472
|
"../../node_modules/.pnpm/eventsource-parser@1.1.2/node_modules/eventsource-parser/dist/index.cjs"(exports) {
|
|
@@ -10029,7 +10132,12 @@ var require_client = __commonJS({
|
|
|
10029
10132
|
var crypto_1 = __require("crypto");
|
|
10030
10133
|
var zlib_1 = __require("zlib");
|
|
10031
10134
|
var index_1 = require_auth();
|
|
10135
|
+
var device_1 = require_device();
|
|
10032
10136
|
var DEFAULT_API_BASE = "https://api.nexrall.com";
|
|
10137
|
+
function deviceHeader() {
|
|
10138
|
+
const device = (0, device_1.getDeviceId)();
|
|
10139
|
+
return device ? { "X-Device-Id": device } : {};
|
|
10140
|
+
}
|
|
10033
10141
|
function resolveApiBase() {
|
|
10034
10142
|
const raw = process.env.NEXRALL_API_BASE?.trim();
|
|
10035
10143
|
if (!raw)
|
|
@@ -10067,10 +10175,15 @@ var require_client = __commonJS({
|
|
|
10067
10175
|
if (!token) {
|
|
10068
10176
|
throw new Error("Not authenticated. Run `nexrall-code login` first.");
|
|
10069
10177
|
}
|
|
10070
|
-
|
|
10178
|
+
const headers = {
|
|
10071
10179
|
"Content-Type": "application/json",
|
|
10072
|
-
Authorization: `Bearer ${token}
|
|
10180
|
+
Authorization: `Bearer ${token}`,
|
|
10181
|
+
// Device evidence (migration 169): sha256 of this machine's value, sent on
|
|
10182
|
+
// every authenticated call so the backend can cluster sessions by machine.
|
|
10183
|
+
// Omitted — never fabricated — when nothing can be resolved.
|
|
10184
|
+
...deviceHeader()
|
|
10073
10185
|
};
|
|
10186
|
+
return headers;
|
|
10074
10187
|
}
|
|
10075
10188
|
var _refreshInFlight = null;
|
|
10076
10189
|
async function refreshAccessToken3() {
|
|
@@ -10083,7 +10196,7 @@ var require_client = __commonJS({
|
|
|
10083
10196
|
try {
|
|
10084
10197
|
const res = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/refresh`, {
|
|
10085
10198
|
method: "POST",
|
|
10086
|
-
headers: { "Content-Type": "application/json" },
|
|
10199
|
+
headers: { "Content-Type": "application/json", ...deviceHeader() },
|
|
10087
10200
|
body: JSON.stringify({ refreshToken })
|
|
10088
10201
|
});
|
|
10089
10202
|
if (!res.ok)
|
|
@@ -10862,7 +10975,7 @@ var require_client = __commonJS({
|
|
|
10862
10975
|
try {
|
|
10863
10976
|
await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/logout`, {
|
|
10864
10977
|
method: "POST",
|
|
10865
|
-
headers: { "Content-Type": "application/json" },
|
|
10978
|
+
headers: { "Content-Type": "application/json", ...deviceHeader() },
|
|
10866
10979
|
body: JSON.stringify({ refreshToken })
|
|
10867
10980
|
});
|
|
10868
10981
|
} catch {
|
|
@@ -11010,7 +11123,7 @@ var require_client = __commonJS({
|
|
|
11010
11123
|
async function exchangeVscodeCode(code) {
|
|
11011
11124
|
const response = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/vscode-exchange`, {
|
|
11012
11125
|
method: "POST",
|
|
11013
|
-
headers: { "Content-Type": "application/json" },
|
|
11126
|
+
headers: { "Content-Type": "application/json", ...deviceHeader() },
|
|
11014
11127
|
body: JSON.stringify({ code })
|
|
11015
11128
|
});
|
|
11016
11129
|
if (!response.ok) {
|
|
@@ -11029,7 +11142,7 @@ var require_client = __commonJS({
|
|
|
11029
11142
|
async function login2(email, password) {
|
|
11030
11143
|
const response = await (0, node_fetch_1.default)(`${exports.API_BASE}/api/auth/login/email`, {
|
|
11031
11144
|
method: "POST",
|
|
11032
|
-
headers: { "Content-Type": "application/json" },
|
|
11145
|
+
headers: { "Content-Type": "application/json", ...deviceHeader() },
|
|
11033
11146
|
body: JSON.stringify({ email, password })
|
|
11034
11147
|
});
|
|
11035
11148
|
if (!response.ok) {
|
|
@@ -12781,10 +12894,11 @@ var require_planMode = __commonJS({
|
|
|
12781
12894
|
"../core/dist/agent/planMode.js"(exports) {
|
|
12782
12895
|
"use strict";
|
|
12783
12896
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12784
|
-
exports.PLAN_MODE_INSTRUCTIONS = void 0;
|
|
12897
|
+
exports.PLAN_MODE_INSTRUCTIONS = exports.PLAN_PROPOSAL_MAX_CHARS = exports.PLAN_PROPOSAL_MIN_CHARS = exports.EXIT_PLAN_MODE_TOOL_SCHEMA = exports.EXIT_PLAN_MODE_TOOL_NAME = void 0;
|
|
12785
12898
|
exports.isReadOnlyCommand = isReadOnlyCommand;
|
|
12786
12899
|
exports.isReadOnlyPipeline = isReadOnlyPipeline;
|
|
12787
12900
|
exports.checkPlanMode = checkPlanMode;
|
|
12901
|
+
exports.parsePlanProposal = parsePlanProposal;
|
|
12788
12902
|
var READ_ONLY_TOOLS3 = /* @__PURE__ */ new Set([
|
|
12789
12903
|
"read_file",
|
|
12790
12904
|
"search_files",
|
|
@@ -12824,7 +12938,13 @@ var require_planMode = __commonJS({
|
|
|
12824
12938
|
"get_diagnostics",
|
|
12825
12939
|
// Reading output from an ALREADY-running background shell. Starting one is
|
|
12826
12940
|
// gated with the rest of bash below; draining a buffer is not a new effect.
|
|
12827
|
-
"bash_output"
|
|
12941
|
+
"bash_output",
|
|
12942
|
+
// Talking to the USER is not a side effect on the repo. Plan mode used to refuse
|
|
12943
|
+
// these, so a model facing an ambiguous request could neither ask nor stop — it
|
|
12944
|
+
// guessed and wrote the plan in one go (user report). ask_user_question is for
|
|
12945
|
+
// clarifying requirements; exit_plan_mode is how the finished plan is presented.
|
|
12946
|
+
"ask_user_question",
|
|
12947
|
+
"exit_plan_mode"
|
|
12828
12948
|
]);
|
|
12829
12949
|
var READ_ONLY_BASH = /* @__PURE__ */ new Set([
|
|
12830
12950
|
"ls",
|
|
@@ -13168,27 +13288,54 @@ var require_planMode = __commonJS({
|
|
|
13168
13288
|
message: `Plan mode is ON, so \`${tool}\` is unavailable \u2014 this session may not modify anything yet. Do NOT ask for approval: no answer the user gives at this prompt can unlock it, because it is a session-wide lock they control directly. Research with read-only tools and finish by proposing a concrete plan (files to change, in order, and how to verify). The user will exit plan mode to let you carry it out.`
|
|
13169
13289
|
};
|
|
13170
13290
|
}
|
|
13291
|
+
exports.EXIT_PLAN_MODE_TOOL_NAME = "exit_plan_mode";
|
|
13292
|
+
exports.EXIT_PLAN_MODE_TOOL_SCHEMA = {
|
|
13293
|
+
name: exports.EXIT_PLAN_MODE_TOOL_NAME,
|
|
13294
|
+
description: "Present your finished plan to the user for approval and END the turn. Call this once, when research is done and any open questions are answered (use ask_user_question for those first). `plan` is the full markdown plan: findings with file:line, the changes file by file in order, risks, and the exact verify command. The user will approve, adjust or reject it; you cannot continue after calling this. Do not use it to ask a question.",
|
|
13295
|
+
input_schema: {
|
|
13296
|
+
type: "object",
|
|
13297
|
+
properties: {
|
|
13298
|
+
plan: { type: "string", description: "The complete plan, in markdown." }
|
|
13299
|
+
},
|
|
13300
|
+
required: ["plan"]
|
|
13301
|
+
}
|
|
13302
|
+
};
|
|
13303
|
+
exports.PLAN_PROPOSAL_MIN_CHARS = 20;
|
|
13304
|
+
exports.PLAN_PROPOSAL_MAX_CHARS = 12e3;
|
|
13305
|
+
function parsePlanProposal(input) {
|
|
13306
|
+
const plan = typeof input.plan === "string" ? input.plan.trim() : "";
|
|
13307
|
+
if (plan.length < exports.PLAN_PROPOSAL_MIN_CHARS)
|
|
13308
|
+
return { error: "exit_plan_mode needs the full plan in `plan` (an empty or one-word plan cannot be approved). Write it out and call again." };
|
|
13309
|
+
return { plan: plan.slice(0, exports.PLAN_PROPOSAL_MAX_CHARS) };
|
|
13310
|
+
}
|
|
13171
13311
|
exports.PLAN_MODE_INSTRUCTIONS = [
|
|
13172
13312
|
"# PLAN MODE IS ACTIVE",
|
|
13173
13313
|
"",
|
|
13174
13314
|
"This session is READ-ONLY. You cannot edit files, write files, run mutating commands, or start",
|
|
13175
|
-
"background processes. This is enforced outside your control \u2014 it is not a permission prompt
|
|
13176
|
-
"the user cannot approve an exception from inside this turn.",
|
|
13315
|
+
"background processes. This is enforced outside your control \u2014 it is not a permission prompt.",
|
|
13177
13316
|
"",
|
|
13178
13317
|
"Therefore:",
|
|
13179
|
-
'- NEVER ask "shall I go ahead and make this change?" \u2014 you cannot, whatever the answer.',
|
|
13180
13318
|
'- NEVER attempt a mutating tool "just to see" \u2014 it will be refused and wastes the turn.',
|
|
13181
13319
|
"- NEVER try to reach a blocked command another way (a different flag, a script, an interpreter).",
|
|
13320
|
+
'- NEVER ask "shall I go ahead?" in prose \u2014 approval is requested by calling `exit_plan_mode`.',
|
|
13182
13321
|
"",
|
|
13183
|
-
"
|
|
13184
|
-
"1.
|
|
13185
|
-
"2.
|
|
13186
|
-
"
|
|
13187
|
-
"
|
|
13322
|
+
"How to work:",
|
|
13323
|
+
"1. Research with read-only tools.",
|
|
13324
|
+
"2. If the request is ambiguous or there is a real fork between approaches that only the user can",
|
|
13325
|
+
" decide, call `ask_user_question` (2-4 options each) BEFORE planning \u2014 do not guess. Skip it",
|
|
13326
|
+
" for anything you can find out yourself.",
|
|
13327
|
+
"3. When ready, call `exit_plan_mode` with the plan. That ends your turn and shows the user an",
|
|
13328
|
+
" approval prompt. Do not write the plan only as chat text and stop.",
|
|
13329
|
+
"",
|
|
13330
|
+
"The plan must contain:",
|
|
13331
|
+
"- What you found \u2014 the specific files, functions and line numbers that matter.",
|
|
13332
|
+
"- What you propose to change \u2014 file by file, in dependency order.",
|
|
13333
|
+
"- Risks and unknowns \u2014 what could break, what you could not verify, what you assumed.",
|
|
13334
|
+
"- How it will be verified \u2014 the exact build/test/lint command that proves it works.",
|
|
13188
13335
|
"",
|
|
13189
13336
|
'Be concrete. "Update the auth logic" is not a plan; "add a `refreshToken` field to AuthConfig in',
|
|
13190
|
-
'auth/index.ts:42, then thread it through client.ts:118" is.
|
|
13191
|
-
"
|
|
13337
|
+
'auth/index.ts:42, then thread it through client.ts:118" is. If the user only asked a question,',
|
|
13338
|
+
"answer it directly and do not call `exit_plan_mode`."
|
|
13192
13339
|
].join("\n");
|
|
13193
13340
|
}
|
|
13194
13341
|
});
|
|
@@ -13412,7 +13559,12 @@ var require_modePolicy = __commonJS({
|
|
|
13412
13559
|
"get_symbols",
|
|
13413
13560
|
"get_workspace_symbols",
|
|
13414
13561
|
"get_hover",
|
|
13415
|
-
"task"
|
|
13562
|
+
"task",
|
|
13563
|
+
// Asking the user / presenting a plan touch no files. Without these, `ask` and
|
|
13564
|
+
// `edit` modes fell through to the final `return 'ask'` (a permission prompt before
|
|
13565
|
+
// the question itself) and plan mode denied them outright.
|
|
13566
|
+
"ask_user_question",
|
|
13567
|
+
"exit_plan_mode"
|
|
13416
13568
|
]);
|
|
13417
13569
|
exports.FILE_WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
13418
13570
|
"write_file",
|
|
@@ -25327,6 +25479,7 @@ var require_iterationPolicy = __commonJS({
|
|
|
25327
25479
|
case "clean":
|
|
25328
25480
|
case "aborted":
|
|
25329
25481
|
case "reported-elsewhere":
|
|
25482
|
+
case "plan-proposed":
|
|
25330
25483
|
return null;
|
|
25331
25484
|
case "empty-response":
|
|
25332
25485
|
return `
|
|
@@ -27277,6 +27430,10 @@ ${ctx}
|
|
|
27277
27430
|
extraTools: [
|
|
27278
27431
|
...(options.mcpManager?.getAnthropicTools() ?? []).filter((t2) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t2.name).split("__")[0])),
|
|
27279
27432
|
...options._agentMemory ? [iterationPolicy_1.AGENT_MEMORY_TOOL_SCHEMA] : [],
|
|
27433
|
+
// Main agent in plan mode only, and only for a client that HAS an approval gate
|
|
27434
|
+
// (onPlanProposed): sub-agents report to their parent, and a headless run or a
|
|
27435
|
+
// background fork has nobody to approve.
|
|
27436
|
+
...options.planMode && depth === 0 && options.onPlanProposed ? [planMode_1.EXIT_PLAN_MODE_TOOL_SCHEMA] : [],
|
|
27280
27437
|
...options.extraToolSchemas ?? []
|
|
27281
27438
|
],
|
|
27282
27439
|
// Derived from the run's ACTUAL allowlist rather than asserted separately,
|
|
@@ -27361,6 +27518,7 @@ ${extra}` : extra;
|
|
|
27361
27518
|
let compacting = false;
|
|
27362
27519
|
const hardCap = autoContinue ? Math.max(maxIterations, iterationPolicy_1.MAX_ITERATIONS_CEILING) : maxIterations;
|
|
27363
27520
|
let stopReason = "unknown";
|
|
27521
|
+
let planProposed = false;
|
|
27364
27522
|
let completedRounds = 0;
|
|
27365
27523
|
let consecutiveErrorRounds = 0;
|
|
27366
27524
|
let repeatedErrorRounds = 0;
|
|
@@ -27724,6 +27882,20 @@ ${extra}` : extra;
|
|
|
27724
27882
|
options.onToolResult(name, result, void 0, toolMeta);
|
|
27725
27883
|
return { block: { ...block, id }, result };
|
|
27726
27884
|
}
|
|
27885
|
+
if (options.planMode && depth === 0 && name === planMode_1.EXIT_PLAN_MODE_TOOL_NAME) {
|
|
27886
|
+
const parsed = (0, planMode_1.parsePlanProposal)(input);
|
|
27887
|
+
if ("error" in parsed)
|
|
27888
|
+
result = { error: parsed.error };
|
|
27889
|
+
else {
|
|
27890
|
+
planProposed = true;
|
|
27891
|
+
options.onPlanProposed?.(parsed.plan);
|
|
27892
|
+
result = {
|
|
27893
|
+
output: "Plan presented to the user for approval. This turn is over \u2014 do not write anything further; their decision arrives as the next message."
|
|
27894
|
+
};
|
|
27895
|
+
}
|
|
27896
|
+
options.onToolResult(name, result, void 0, toolMeta);
|
|
27897
|
+
return { block: { ...block, id }, result };
|
|
27898
|
+
}
|
|
27727
27899
|
if (options.planMode) {
|
|
27728
27900
|
const refusal = (0, planMode_1.checkPlanMode)(name, input);
|
|
27729
27901
|
if (refusal) {
|
|
@@ -28050,6 +28222,10 @@ ${r2.output}` : `Error: ${r2.error}` : r2.output ?? ""
|
|
|
28050
28222
|
messages.push(toolResultMessage);
|
|
28051
28223
|
completedRounds++;
|
|
28052
28224
|
options.onProgress?.(messages);
|
|
28225
|
+
if (planProposed) {
|
|
28226
|
+
stopReason = "plan-proposed";
|
|
28227
|
+
break;
|
|
28228
|
+
}
|
|
28053
28229
|
if (postBatchStop) {
|
|
28054
28230
|
stopReason = "hook-blocked";
|
|
28055
28231
|
hookBlockedReason = postBatchStop;
|
|
@@ -30721,6 +30897,100 @@ var require_effortCap = __commonJS({
|
|
|
30721
30897
|
}
|
|
30722
30898
|
});
|
|
30723
30899
|
|
|
30900
|
+
// ../core/dist/agent/askUser.js
|
|
30901
|
+
var require_askUser = __commonJS({
|
|
30902
|
+
"../core/dist/agent/askUser.js"(exports) {
|
|
30903
|
+
"use strict";
|
|
30904
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30905
|
+
exports.ASK_USER_TOOL_SCHEMA = exports.ASK_USER_TOOL_NAME = void 0;
|
|
30906
|
+
exports.parseAskUserQuestions = parseAskUserQuestions;
|
|
30907
|
+
exports.formatAskUserResult = formatAskUserResult;
|
|
30908
|
+
exports.ASK_USER_TOOL_NAME = "ask_user_question";
|
|
30909
|
+
exports.ASK_USER_TOOL_SCHEMA = {
|
|
30910
|
+
name: exports.ASK_USER_TOOL_NAME,
|
|
30911
|
+
description: "Ask the user 1-4 multiple-choice questions when a decision is theirs to make (ambiguous requirements, a fork between approaches). Each question has 2-4 options; the user can also type their own answer. Do not use it for things you can find out yourself, or for plan approval.",
|
|
30912
|
+
input_schema: {
|
|
30913
|
+
type: "object",
|
|
30914
|
+
properties: {
|
|
30915
|
+
questions: {
|
|
30916
|
+
type: "array",
|
|
30917
|
+
minItems: 1,
|
|
30918
|
+
maxItems: 4,
|
|
30919
|
+
items: {
|
|
30920
|
+
type: "object",
|
|
30921
|
+
properties: {
|
|
30922
|
+
question: { type: "string", description: "The full question, ending with a question mark." },
|
|
30923
|
+
header: { type: "string", description: "Very short label (max ~12 chars)." },
|
|
30924
|
+
options: {
|
|
30925
|
+
type: "array",
|
|
30926
|
+
minItems: 2,
|
|
30927
|
+
maxItems: 4,
|
|
30928
|
+
items: {
|
|
30929
|
+
type: "object",
|
|
30930
|
+
properties: { label: { type: "string" }, description: { type: "string" } },
|
|
30931
|
+
required: ["label"]
|
|
30932
|
+
}
|
|
30933
|
+
}
|
|
30934
|
+
},
|
|
30935
|
+
required: ["question", "options"]
|
|
30936
|
+
}
|
|
30937
|
+
}
|
|
30938
|
+
},
|
|
30939
|
+
required: ["questions"]
|
|
30940
|
+
}
|
|
30941
|
+
};
|
|
30942
|
+
function parseAskUserQuestions(input) {
|
|
30943
|
+
const raw = input.questions;
|
|
30944
|
+
if (!Array.isArray(raw) || raw.length < 1 || raw.length > 4)
|
|
30945
|
+
return "questions must be an array of 1-4 items";
|
|
30946
|
+
const out = [];
|
|
30947
|
+
for (const q of raw) {
|
|
30948
|
+
const o = q;
|
|
30949
|
+
const question = typeof o?.question === "string" ? o.question.trim() : "";
|
|
30950
|
+
const opts = Array.isArray(o?.options) ? o.options : [];
|
|
30951
|
+
if (!question)
|
|
30952
|
+
return "every question needs non-empty text";
|
|
30953
|
+
if (opts.length < 2 || opts.length > 4)
|
|
30954
|
+
return `"${question}" needs 2-4 options`;
|
|
30955
|
+
const options = [];
|
|
30956
|
+
for (const x2 of opts) {
|
|
30957
|
+
const label = typeof x2?.label === "string" ? x2.label.trim() : "";
|
|
30958
|
+
if (!label)
|
|
30959
|
+
return `"${question}" has an option without a label`;
|
|
30960
|
+
const d = x2.description;
|
|
30961
|
+
options.push({ label, description: typeof d === "string" && d.trim() ? d.trim() : void 0 });
|
|
30962
|
+
}
|
|
30963
|
+
out.push({ question, header: typeof o.header === "string" ? o.header.trim().slice(0, 14) : void 0, options });
|
|
30964
|
+
}
|
|
30965
|
+
return out;
|
|
30966
|
+
}
|
|
30967
|
+
function formatAskUserResult(qs, answers) {
|
|
30968
|
+
const dismissed = {
|
|
30969
|
+
error: "The user dismissed the question without answering. Do not assume an answer; proceed on your best judgement or stop and say what you need."
|
|
30970
|
+
};
|
|
30971
|
+
if (!answers || answers.length !== qs.length)
|
|
30972
|
+
return dismissed;
|
|
30973
|
+
const parts = [];
|
|
30974
|
+
for (let i2 = 0; i2 < qs.length; i2++) {
|
|
30975
|
+
const a = answers[i2];
|
|
30976
|
+
const q = qs[i2];
|
|
30977
|
+
if ("text" in a) {
|
|
30978
|
+
const t2 = String(a.text ?? "").trim();
|
|
30979
|
+
if (!t2)
|
|
30980
|
+
return dismissed;
|
|
30981
|
+
parts.push(`"${q.question}" = "${t2}" (user's own answer)`);
|
|
30982
|
+
} else {
|
|
30983
|
+
const opt = q.options[a.index];
|
|
30984
|
+
if (!opt)
|
|
30985
|
+
return dismissed;
|
|
30986
|
+
parts.push(`"${q.question}" = "${opt.label}"`);
|
|
30987
|
+
}
|
|
30988
|
+
}
|
|
30989
|
+
return { output: "User answered: " + parts.join("; ") };
|
|
30990
|
+
}
|
|
30991
|
+
}
|
|
30992
|
+
});
|
|
30993
|
+
|
|
30724
30994
|
// ../core/dist/permissions/autoMode.js
|
|
30725
30995
|
var require_autoMode = __commonJS({
|
|
30726
30996
|
"../core/dist/permissions/autoMode.js"(exports) {
|
|
@@ -47845,6 +48115,7 @@ var require_dist2 = __commonJS({
|
|
|
47845
48115
|
__exportStar(require_safeSlice(), exports);
|
|
47846
48116
|
__exportStar(require_cellWidth(), exports);
|
|
47847
48117
|
__exportStar(require_auth(), exports);
|
|
48118
|
+
__exportStar(require_device(), exports);
|
|
47848
48119
|
__exportStar(require_client(), exports);
|
|
47849
48120
|
__exportStar(require_executor(), exports);
|
|
47850
48121
|
__exportStar(require_loop(), exports);
|
|
@@ -47872,6 +48143,7 @@ var require_dist2 = __commonJS({
|
|
|
47872
48143
|
__exportStar(require_agentTypes(), exports);
|
|
47873
48144
|
__exportStar(require_effortCap(), exports);
|
|
47874
48145
|
__exportStar(require_planMode(), exports);
|
|
48146
|
+
__exportStar(require_askUser(), exports);
|
|
47875
48147
|
__exportStar(require_agentRegistry(), exports);
|
|
47876
48148
|
__exportStar(require_backgroundAgents(), exports);
|
|
47877
48149
|
__exportStar(require_rules(), exports);
|
|
@@ -79778,11 +80050,14 @@ function openUrl(url) {
|
|
|
79778
80050
|
} catch {
|
|
79779
80051
|
}
|
|
79780
80052
|
}
|
|
80053
|
+
function fmtCostUsd(cost) {
|
|
80054
|
+
return `$${cost < 0.01 ? cost.toFixed(4) : cost.toFixed(2)}`;
|
|
80055
|
+
}
|
|
79781
80056
|
function deliverableBits(s2) {
|
|
79782
80057
|
const bits = [
|
|
79783
80058
|
s2.branch ? `branch ${s2.branch}` : "",
|
|
79784
80059
|
s2.prUrl ?? "",
|
|
79785
|
-
s2.costUsd != null ?
|
|
80060
|
+
s2.costUsd != null ? fmtCostUsd(s2.costUsd) : ""
|
|
79786
80061
|
].filter(Boolean);
|
|
79787
80062
|
return bits.join(" \xB7 ");
|
|
79788
80063
|
}
|
|
@@ -79848,10 +80123,13 @@ async function cloudLogsCommand(id, opts) {
|
|
|
79848
80123
|
console.log(JSON.stringify(r2, null, 2));
|
|
79849
80124
|
return;
|
|
79850
80125
|
}
|
|
80126
|
+
if (r2.note) {
|
|
80127
|
+
console.log(paint.dim(` ${r2.note}`));
|
|
80128
|
+
}
|
|
79851
80129
|
if (r2.log) {
|
|
79852
80130
|
console.log(r2.log.endsWith("\n") ? r2.log.slice(0, -1) : r2.log);
|
|
79853
|
-
} else {
|
|
79854
|
-
console.log(paint.dim(
|
|
80131
|
+
} else if (!r2.note) {
|
|
80132
|
+
console.log(paint.dim(" No log available."));
|
|
79855
80133
|
}
|
|
79856
80134
|
return;
|
|
79857
80135
|
}
|
|
@@ -104567,6 +104845,7 @@ function nextBackgroundChange() {
|
|
|
104567
104845
|
}
|
|
104568
104846
|
var _mcpManager = null;
|
|
104569
104847
|
var _askIO = null;
|
|
104848
|
+
var _proposedPlan = null;
|
|
104570
104849
|
var _elicitationHandler = null;
|
|
104571
104850
|
var _mcpConfig = null;
|
|
104572
104851
|
var sessionTouched = [];
|
|
@@ -105085,6 +105364,9 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
|
|
|
105085
105364
|
// makes it real: the loop refuses mutating tools before the permission
|
|
105086
105365
|
// prompt, and sub-agents inherit the lock.
|
|
105087
105366
|
planMode: mode === "plan",
|
|
105367
|
+
onPlanProposed: (plan) => {
|
|
105368
|
+
_proposedPlan = plan;
|
|
105369
|
+
},
|
|
105088
105370
|
// Same "enforced, not just hinted" reasoning as planMode directly above:
|
|
105089
105371
|
// when set, core's checkWorktreeIsolation refuses any write/bash-cwd/git-
|
|
105090
105372
|
// redirect that targets outside this worktree, BEFORE the permission
|
|
@@ -110874,6 +111156,8 @@ ${text}
|
|
|
110874
111156
|
agentRunning = false;
|
|
110875
111157
|
commitCheckpoint();
|
|
110876
111158
|
}
|
|
111159
|
+
if (_proposedPlan !== null && !abortSignal.aborted)
|
|
111160
|
+
await resolveProposedPlan();
|
|
110877
111161
|
if (activeGoal && !abortSignal.aborted) {
|
|
110878
111162
|
rl.pause();
|
|
110879
111163
|
await runGoalLoop();
|
|
@@ -110884,6 +111168,75 @@ ${text}
|
|
|
110884
111168
|
deliverHookWakeWhenIdle();
|
|
110885
111169
|
void maybeSuggestNextPrompt(turnOk && !abortSignal.aborted);
|
|
110886
111170
|
};
|
|
111171
|
+
async function resolveProposedPlan() {
|
|
111172
|
+
while (_proposedPlan !== null) {
|
|
111173
|
+
const plan = _proposedPlan;
|
|
111174
|
+
_proposedPlan = null;
|
|
111175
|
+
console.log();
|
|
111176
|
+
console.log(paint.bold(" Proposed plan"));
|
|
111177
|
+
for (const l of plan.split("\n"))
|
|
111178
|
+
console.log(" " + l);
|
|
111179
|
+
console.log();
|
|
111180
|
+
if (modeLocked || !interactive || !(rl instanceof InkReadlineAdapter))
|
|
111181
|
+
return;
|
|
111182
|
+
const pick = await rl.askChoice({
|
|
111183
|
+
title: "Ready to implement this plan?",
|
|
111184
|
+
detail: "plan mode is read-only \xB7 approving leaves it",
|
|
111185
|
+
options: [
|
|
111186
|
+
{ key: "1", label: "Yes \u2014 auto-accept edits (edit mode; still asks before shell commands)" },
|
|
111187
|
+
{ key: "2", label: "Yes \u2014 ask before each edit (ask mode)" },
|
|
111188
|
+
{ key: "3", label: "No \u2014 keep planning (tell me what to change)" }
|
|
111189
|
+
],
|
|
111190
|
+
risk: "low"
|
|
111191
|
+
});
|
|
111192
|
+
let prompt2;
|
|
111193
|
+
if (pick === 0 || pick === 1) {
|
|
111194
|
+
agentMode = pick === 0 ? "edit" : "ask";
|
|
111195
|
+
updateFooter();
|
|
111196
|
+
console.log(paint.green(` Mode \u2192 ${paint.bold(agentMode)}`));
|
|
111197
|
+
prompt2 = "The user approved this plan. Implement it now, following the steps in order and running the verification at the end.\n\n" + plan;
|
|
111198
|
+
} else {
|
|
111199
|
+
const fb = (await new Promise((res) => rl.question(" What should change? (Enter to skip) ", res))).trim();
|
|
111200
|
+
if (!fb)
|
|
111201
|
+
return;
|
|
111202
|
+
prompt2 = fb;
|
|
111203
|
+
}
|
|
111204
|
+
messages.push({ role: "user", content: [{ type: "text", text: prompt2 }] });
|
|
111205
|
+
rl.pause();
|
|
111206
|
+
abortSignal.aborted = false;
|
|
111207
|
+
agentRunning = true;
|
|
111208
|
+
try {
|
|
111209
|
+
const result = await runTurn(
|
|
111210
|
+
messages,
|
|
111211
|
+
modelAlias,
|
|
111212
|
+
workDir,
|
|
111213
|
+
abortSignal,
|
|
111214
|
+
env3,
|
|
111215
|
+
nexrallMd,
|
|
111216
|
+
agentMode,
|
|
111217
|
+
effortLevel,
|
|
111218
|
+
checkpoints,
|
|
111219
|
+
saveProgress,
|
|
111220
|
+
takePendingInput,
|
|
111221
|
+
worktreeState,
|
|
111222
|
+
peerCtx
|
|
111223
|
+
);
|
|
111224
|
+
messages = result.messages;
|
|
111225
|
+
updateTitle();
|
|
111226
|
+
saveSession2(sessionId, sessionTitle, workDir, messages);
|
|
111227
|
+
} catch (err) {
|
|
111228
|
+
messages = recoverTurn(err, messages);
|
|
111229
|
+
updateTitle();
|
|
111230
|
+
saveSession2(sessionId, sessionTitle || "Untitled", workDir, messages);
|
|
111231
|
+
_proposedPlan = null;
|
|
111232
|
+
} finally {
|
|
111233
|
+
agentRunning = false;
|
|
111234
|
+
commitCheckpoint();
|
|
111235
|
+
}
|
|
111236
|
+
if (abortSignal.aborted)
|
|
111237
|
+
_proposedPlan = null;
|
|
111238
|
+
}
|
|
111239
|
+
}
|
|
110887
111240
|
rl.on("line", (rawLine) => {
|
|
110888
111241
|
void handleLine(rawLine);
|
|
110889
111242
|
});
|