nexrall-code 0.5.133 → 0.5.134

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.
Files changed (2) hide show
  1. package/dist/index.js +400 -60
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3382,7 +3382,7 @@ var require_dist = __commonJS({
3382
3382
  return;
3383
3383
  }
3384
3384
  const noValue = fieldLength < 0;
3385
- const field = lineBuffer.slice(index, index + (noValue ? lineLength : fieldLength));
3385
+ const field2 = lineBuffer.slice(index, index + (noValue ? lineLength : fieldLength));
3386
3386
  let step = 0;
3387
3387
  if (noValue) {
3388
3388
  step = lineLength;
@@ -3394,13 +3394,13 @@ var require_dist = __commonJS({
3394
3394
  const position = index + step;
3395
3395
  const valueLength = lineLength - step;
3396
3396
  const value = lineBuffer.slice(position, position + valueLength).toString();
3397
- if (field === "data") {
3397
+ if (field2 === "data") {
3398
3398
  data += value ? "".concat(value, "\n") : "\n";
3399
- } else if (field === "event") {
3399
+ } else if (field2 === "event") {
3400
3400
  eventName = value;
3401
- } else if (field === "id" && !value.includes("\0")) {
3401
+ } else if (field2 === "id" && !value.includes("\0")) {
3402
3402
  eventId = value;
3403
- } else if (field === "retry") {
3403
+ } else if (field2 === "retry") {
3404
3404
  const retry = parseInt(value, 10);
3405
3405
  if (!Number.isNaN(retry)) {
3406
3406
  onParse({
@@ -3728,9 +3728,9 @@ var require_ponyfill_es2018 = __commonJS({
3728
3728
  throw new TypeError(`Parameter ${position} is required in '${context}'.`);
3729
3729
  }
3730
3730
  }
3731
- function assertRequiredField(x2, field, context) {
3731
+ function assertRequiredField(x2, field2, context) {
3732
3732
  if (x2 === void 0) {
3733
- throw new TypeError(`${field} is required in '${context}'.`);
3733
+ throw new TypeError(`${field2} is required in '${context}'.`);
3734
3734
  }
3735
3735
  }
3736
3736
  function convertUnrestrictedDouble(value) {
@@ -68109,8 +68109,8 @@ var require_FieldFormatter = __commonJS({
68109
68109
  }
68110
68110
  return false;
68111
68111
  }
68112
- format(field, fieldIndex, isHeader) {
68113
- const preparedField = `${lodash_isnil_1.default(field) ? "" : field}`.replace(/\0/g, "");
68112
+ format(field2, fieldIndex, isHeader) {
68113
+ const preparedField = `${lodash_isnil_1.default(field2) ? "" : field2}`.replace(/\0/g, "");
68114
68114
  const { formatterOptions } = this;
68115
68115
  if (formatterOptions.quote !== "") {
68116
68116
  const shouldEscape = preparedField.indexOf(formatterOptions.quote) !== -1;
@@ -68124,9 +68124,9 @@ var require_FieldFormatter = __commonJS({
68124
68124
  }
68125
68125
  return preparedField;
68126
68126
  }
68127
- quoteField(field) {
68127
+ quoteField(field2) {
68128
68128
  const { quote } = this.formatterOptions;
68129
- return `${quote}${field}${quote}`;
68129
+ return `${quote}${field2}${quote}`;
68130
68130
  }
68131
68131
  };
68132
68132
  exports2.FieldFormatter = FieldFormatter;
@@ -68292,7 +68292,7 @@ var require_RowFormatter = __commonJS({
68292
68292
  return this._rowTransform(row, cb);
68293
68293
  }
68294
68294
  formatColumns(columns, isHeadersRow) {
68295
- const formattedCols = columns.map((field, i2) => this.fieldFormatter.format(field, i2, isHeadersRow)).join(this.formatterOptions.delimiter);
68295
+ const formattedCols = columns.map((field2, i2) => this.fieldFormatter.format(field2, i2, isHeadersRow)).join(this.formatterOptions.delimiter);
68296
68296
  const { rowCount } = this;
68297
68297
  this.rowCount += 1;
68298
68298
  if (rowCount) {
@@ -77951,8 +77951,8 @@ var require_common2 = __commonJS({
77951
77951
  exports2.mark = mark;
77952
77952
  exports2.isIgnored = isIgnored;
77953
77953
  exports2.childrenIgnored = childrenIgnored;
77954
- function ownProp(obj2, field) {
77955
- return Object.prototype.hasOwnProperty.call(obj2, field);
77954
+ function ownProp(obj2, field2) {
77955
+ return Object.prototype.hasOwnProperty.call(obj2, field2);
77956
77956
  }
77957
77957
  var fs15 = __require("fs");
77958
77958
  var path15 = __require("path");
@@ -105961,14 +105961,14 @@ var require_destructive = __commonJS({
105961
105961
  return null;
105962
105962
  }
105963
105963
  var MAX_DEPTH = 4;
105964
- function classify(command, depth) {
105964
+ function classify2(command, depth) {
105965
105965
  if (depth > MAX_DEPTH)
105966
105966
  return null;
105967
105967
  const viaRegex = classifyRegex(command);
105968
105968
  if (viaRegex)
105969
105969
  return viaRegex;
105970
105970
  for (const tokens of (0, destructiveTokens_1.tokenize)(stripHeredocs(command))) {
105971
- const m2 = (0, destructiveTokens_1.classifyTokens)(tokens, (inner) => classify(inner, depth + 1), classifyRawSql);
105971
+ const m2 = (0, destructiveTokens_1.classifyTokens)(tokens, (inner) => classify2(inner, depth + 1), classifyRawSql);
105972
105972
  if (m2)
105973
105973
  return m2;
105974
105974
  }
@@ -105977,7 +105977,7 @@ var require_destructive = __commonJS({
105977
105977
  function classifyDestructiveCommand(command) {
105978
105978
  if (!command || typeof command !== "string")
105979
105979
  return null;
105980
- return classify(command, 0);
105980
+ return classify2(command, 0);
105981
105981
  }
105982
105982
  function isDestructiveBash3(tool, input) {
105983
105983
  if (tool !== "bash" && tool !== "run_command" && tool !== "execute_command")
@@ -146066,6 +146066,269 @@ function paintLine(l2) {
146066
146066
  }
146067
146067
  }
146068
146068
 
146069
+ // src/ui/errorView.ts
146070
+ var TITLE_MAX = 120;
146071
+ var DETAIL_MAX = 300;
146072
+ function clip(s2, max) {
146073
+ const flat = s2.replace(/\s+/g, " ").trim();
146074
+ return flat.length <= max ? flat : flat.slice(0, max - 1) + "\u2026";
146075
+ }
146076
+ function messageOf(err) {
146077
+ let msg;
146078
+ if (typeof err === "string")
146079
+ msg = err;
146080
+ else if (err instanceof Error)
146081
+ msg = err.message || (err.name && err.name !== "Error" ? err.name : "");
146082
+ else if (err && typeof err === "object" && typeof err.message === "string")
146083
+ msg = err.message;
146084
+ else
146085
+ msg = String(err);
146086
+ const brace = msg.indexOf("{");
146087
+ if (brace > 0) {
146088
+ const tail = msg.slice(brace);
146089
+ try {
146090
+ const parsed = JSON.parse(tail);
146091
+ const inner = typeof parsed.error === "string" ? parsed.error : typeof parsed.message === "string" ? parsed.message : "";
146092
+ if (inner)
146093
+ return `${msg.slice(0, brace).replace(/[:,\s]+$/, "")}: ${inner}`.trim();
146094
+ } catch {
146095
+ }
146096
+ }
146097
+ return msg;
146098
+ }
146099
+ function codesOf(err) {
146100
+ const out = [];
146101
+ const push = (v) => {
146102
+ if (v && typeof v === "object") {
146103
+ const rec = v;
146104
+ if (typeof rec.code === "string")
146105
+ out.push(rec.code);
146106
+ if (typeof rec.errno === "string")
146107
+ out.push(rec.errno);
146108
+ if (typeof rec.name === "string")
146109
+ out.push(rec.name);
146110
+ if (rec.cause)
146111
+ push(rec.cause);
146112
+ }
146113
+ };
146114
+ push(err);
146115
+ return out;
146116
+ }
146117
+ function field(err, key) {
146118
+ if (err && typeof err === "object" && key in err) {
146119
+ return err[key];
146120
+ }
146121
+ return void 0;
146122
+ }
146123
+ var NETWORK_RE = /fetch failed|econnrefused|econnreset|enotfound|eai_again|etimedout|socket hang up|network error|getaddrinfo|tls|ssl|proxy|socket disconnected|terminated|other side closed/i;
146124
+ var NETWORK_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ECONNRESET", "ENOTFOUND", "EAI_AGAIN", "ETIMEDOUT", "UND_ERR_SOCKET", "UND_ERR_CONNECT_TIMEOUT", "EHOSTUNREACH", "ENETUNREACH"]);
146125
+ var CONTEXT_RE = /context (?:length|window)|too many tokens|maximum context|token limit|prompt is too long|exceeds the maximum|max_tokens|input length/i;
146126
+ var MODEL_RE = /unknown model|model[^.]{0,20}not found|invalid model|model[^.]{0,20}does not exist|model[^.]{0,20}not available|no such model|unsupported model/i;
146127
+ var AUTH_RE = /not authenticated|unauthorized|invalid (?:api )?(?:key|token)|authentication (?:failed|required)|sign in|login required|token (?:expired|revoked)/i;
146128
+ var BALANCE_RE = /insufficient (?:balance|credit|funds)|out of (?:credit|funds|balance)|balance (?:is )?too low|no credit|quota exceeded|payment required/i;
146129
+ var RATE_RE = /rate ?limit|too many requests|overloaded/i;
146130
+ function describeError(err, opts = {}) {
146131
+ const view = classify(err, opts);
146132
+ if (opts.debugLogPath && view.kind !== "abort")
146133
+ view.hints.push(`Full trace: ${opts.debugLogPath}`);
146134
+ return view;
146135
+ }
146136
+ function classify(err, opts) {
146137
+ const msg = messageOf(err);
146138
+ const status = field(err, "status");
146139
+ const code = field(err, "code");
146140
+ const names = codesOf(err);
146141
+ const hay = `${msg} ${names.join(" ")}`;
146142
+ const name = field(err, "name");
146143
+ if (opts.aborted || name === "AbortError" || names.includes("AbortError") || /^(Aborted|Request aborted)$/i.test(msg)) {
146144
+ return { kind: "abort", title: "Interrupted", hints: [] };
146145
+ }
146146
+ if (code === "team_budget_exhausted") {
146147
+ return {
146148
+ kind: "team-budget",
146149
+ title: "This team pool is out of budget",
146150
+ hints: ["Bill your own wallet instead: /team use personal", "Or ask a team admin to top the shared budget up"]
146151
+ };
146152
+ }
146153
+ if (status === 402 || BALANCE_RE.test(msg)) {
146154
+ return {
146155
+ kind: "balance",
146156
+ title: "Out of credit \u2014 the turn could not start",
146157
+ hints: ["Top up your wallet, then send the message again", "/balance shows what is left; /team switches who pays"]
146158
+ };
146159
+ }
146160
+ if (field(err, "authExpired") || status === 401 || AUTH_RE.test(msg)) {
146161
+ return {
146162
+ kind: "auth",
146163
+ title: "Not signed in",
146164
+ hints: ["Run `nexrall-code login` to sign in again", "Your session keeps its place: /resume picks it back up"]
146165
+ };
146166
+ }
146167
+ if (status === 429 || RATE_RE.test(msg)) {
146168
+ return {
146169
+ kind: "rate-limit",
146170
+ title: "Rate limited by the API",
146171
+ hints: ["Transient overload \u2014 already retried a few times automatically", "Send the message again, or /model to switch models"]
146172
+ };
146173
+ }
146174
+ if (status === 404 && MODEL_RE.test(msg)) {
146175
+ return { kind: "model", title: clip(msg, TITLE_MAX), hints: ["/model lists what this account can use"] };
146176
+ }
146177
+ if (MODEL_RE.test(msg)) {
146178
+ return { kind: "model", title: clip(msg, TITLE_MAX), hints: ["/model lists what this account can use"] };
146179
+ }
146180
+ if (CONTEXT_RE.test(msg)) {
146181
+ return {
146182
+ kind: "context",
146183
+ title: "Context window is full",
146184
+ hints: ["/compact summarizes the conversation to make room", "/clear starts a fresh session"]
146185
+ };
146186
+ }
146187
+ if (field(err, "name") === "ToolNotAllowedError") {
146188
+ return { kind: "tool", title: clip(msg, TITLE_MAX), hints: ["/permissions shows which tools are allowed, asked for, or denied"] };
146189
+ }
146190
+ if (typeof status === "number" && status >= 500 || /\bAPI error 5\d\d\b/.test(msg)) {
146191
+ return {
146192
+ kind: "server",
146193
+ title: `Nexrall API error (${typeof status === "number" ? status : "5xx"})`,
146194
+ hints: ["Usually transient \u2014 send the message again", "`nex doctor` checks login, model and connectivity"]
146195
+ };
146196
+ }
146197
+ if (NETWORK_RE.test(hay) || names.some((n) => NETWORK_CODES.has(n))) {
146198
+ return {
146199
+ kind: "network",
146200
+ title: "Cannot reach the Nexrall API",
146201
+ hints: ["Check your connection, VPN or proxy", "`nex doctor` shows what is reachable and with which credentials"]
146202
+ };
146203
+ }
146204
+ const view = {
146205
+ kind: "other",
146206
+ title: clip(msg, TITLE_MAX) || "The turn failed",
146207
+ hints: ["Send the message again to retry", "`nex doctor` checks your setup"]
146208
+ };
146209
+ return view;
146210
+ }
146211
+ function errorDetail(err, view) {
146212
+ if (view.kind !== "other")
146213
+ return null;
146214
+ const msg = messageOf(err);
146215
+ const title = view.title;
146216
+ if (!msg || msg === title || title.endsWith(msg))
146217
+ return null;
146218
+ return clip(msg, DETAIL_MAX);
146219
+ }
146220
+
146221
+ // src/ui/turnSummary.ts
146222
+ var FILE_TOOLS = /* @__PURE__ */ new Set([
146223
+ "write_file",
146224
+ "write_docx",
146225
+ "write_xlsx",
146226
+ "write_pptx",
146227
+ "edit_file",
146228
+ "multi_edit",
146229
+ "delete_file",
146230
+ "notebook_edit",
146231
+ "move_file",
146232
+ "copy_file"
146233
+ ]);
146234
+ var TEST_RE = /(^|[\s;&|])(npm (?:run )?(?:test|t)\b|yarn test\b|pnpm (?:run )?test\b|bun test\b|jest\b|vitest\b|mocha\b|pytest\b|python3? -m pytest\b|go test\b|cargo test\b|rspec\b|phpunit\b|dotnet test\b|mvn (?:test|verify)\b|gradle test\b|make test\b|swift test\b)/i;
146235
+ function freshTurnStats(now = Date.now()) {
146236
+ return { startedAt: now, endedAt: now, rounds: 0, toolCalls: 0, files: [], testRuns: [], costKnown: false };
146237
+ }
146238
+ function pathsTouched(name, input) {
146239
+ if (!FILE_TOOLS.has(name) || !input || typeof input !== "object")
146240
+ return [];
146241
+ const rec = input;
146242
+ const str = (v) => typeof v === "string" && v ? [v] : [];
146243
+ if (name === "move_file")
146244
+ return [...str(rec.source), ...str(rec.dest)];
146245
+ if (name === "copy_file")
146246
+ return str(rec.destination);
146247
+ return str(rec.path);
146248
+ }
146249
+ function noteToolUse(stats, name, input, subAgent = false) {
146250
+ if (subAgent)
146251
+ return;
146252
+ stats.toolCalls++;
146253
+ for (const p of pathsTouched(name, input)) {
146254
+ if (!stats.files.includes(p))
146255
+ stats.files.push(p);
146256
+ }
146257
+ }
146258
+ function isTestCommand(command) {
146259
+ return TEST_RE.test(command);
146260
+ }
146261
+ function noteBashResult(stats, command, ok) {
146262
+ if (!isTestCommand(command))
146263
+ return;
146264
+ const label = command.replace(/\s+/g, " ").trim().slice(0, 60);
146265
+ const existing = stats.testRuns.find((t2) => t2.label === label);
146266
+ if (existing)
146267
+ existing.ok = ok;
146268
+ else
146269
+ stats.testRuns.push({ label, ok });
146270
+ }
146271
+ function formatDuration(ms) {
146272
+ const clamped = Math.max(0, ms);
146273
+ if (clamped < 1e3)
146274
+ return `${Math.round(clamped)}ms`;
146275
+ const s2 = clamped / 1e3;
146276
+ if (s2 < 60)
146277
+ return `${s2 < 10 ? s2.toFixed(1) : Math.round(s2)}s`;
146278
+ const m2 = Math.floor(s2 / 60);
146279
+ const rest = Math.round(s2 - m2 * 60);
146280
+ if (m2 < 60)
146281
+ return rest ? `${m2}m ${rest}s` : `${m2}m`;
146282
+ const h2 = Math.floor(m2 / 60);
146283
+ const rem = m2 - h2 * 60;
146284
+ return rem ? `${h2}h ${rem}m` : `${h2}h`;
146285
+ }
146286
+ function formatTokens(n) {
146287
+ if (n < 1e3)
146288
+ return String(n);
146289
+ if (n < 1e6)
146290
+ return `${(n / 1e3).toFixed(n < 1e5 ? 1 : 0)}k`;
146291
+ return `${(n / 1e6).toFixed(1)}M`;
146292
+ }
146293
+ function formatCost(usd) {
146294
+ if (usd > 0 && usd < 0.01)
146295
+ return `$${usd.toFixed(4)}`;
146296
+ return `$${usd.toFixed(2)}`;
146297
+ }
146298
+ function summaryLines2(stats, opts) {
146299
+ const { outcome } = opts;
146300
+ const elapsed = formatDuration(stats.endedAt - stats.startedAt);
146301
+ const parts = [];
146302
+ if (outcome === "failed")
146303
+ parts.push("stopped early");
146304
+ if (outcome === "interrupted")
146305
+ parts.push("interrupted");
146306
+ const n = stats.files.length;
146307
+ if (n)
146308
+ parts.push(`${n} file${n === 1 ? "" : "s"} changed`);
146309
+ if (stats.toolCalls)
146310
+ parts.push(`${stats.toolCalls} tool call${stats.toolCalls === 1 ? "" : "s"}`);
146311
+ if (stats.tokens) {
146312
+ const cached = stats.cachedTokens ?? 0;
146313
+ const pct = cached > 0 ? ` (cached ${Math.round(cached / stats.tokens * 100)}%)` : "";
146314
+ parts.push(`${formatTokens(stats.tokens)} tokens${pct}`);
146315
+ }
146316
+ if (stats.costKnown && stats.costUsd)
146317
+ parts.push(formatCost(stats.costUsd));
146318
+ parts.push(elapsed);
146319
+ const head = parts.join(" \xB7 ");
146320
+ const lines = [head];
146321
+ const failedTests = stats.testRuns.filter((t2) => !t2.ok);
146322
+ for (const t2 of failedTests)
146323
+ lines.push(`tests: \u2717 ${t2.label}`);
146324
+ if (failedTests.length === 0) {
146325
+ for (const t2 of stats.testRuns)
146326
+ lines.push(`tests: \u2713 ${t2.label}`);
146327
+ }
146328
+ const cols = opts.cols && opts.cols > 20 ? opts.cols - 4 : 0;
146329
+ return cols ? lines.map((l2) => l2.length > cols ? l2.slice(0, cols - 1) + "\u2026" : l2) : lines;
146330
+ }
146331
+
146069
146332
  // src/commands/chat.ts
146070
146333
  import * as os9 from "os";
146071
146334
  import * as crypto4 from "crypto";
@@ -147036,23 +147299,39 @@ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, ne
147036
147299
  };
147037
147300
  }
147038
147301
 
147302
+ // src/ui/previewCap.ts
147303
+ var MAX_PREVIEW_LINES = 24;
147304
+ function previewCap(total, max = MAX_PREVIEW_LINES) {
147305
+ const show = Math.max(0, Math.min(total, max));
147306
+ return { show, hidden: Math.max(0, total - show) };
147307
+ }
147308
+ function hiddenNote(hidden) {
147309
+ return `\u2026 ${hidden} more line${hidden === 1 ? "" : "s"} not shown`;
147310
+ }
147311
+
147039
147312
  // src/ui/diff.ts
147040
147313
  function showDiff(filePath2, oldContent, newContent) {
147041
147314
  const isNew = oldContent === null;
147042
147315
  const from = isNew ? "/dev/null" : `a/${filePath2}`;
147043
147316
  const to = `b/${filePath2}`;
147044
- console.log();
147045
- console.log(source_default.bold(`--- ${from}`));
147046
- console.log(source_default.bold(`+++ ${to}`));
147317
+ const emit = (head, body2) => {
147318
+ for (const line of head)
147319
+ console.log(line);
147320
+ const { show, hidden } = previewCap(body2.length);
147321
+ for (const line of body2.slice(0, show))
147322
+ console.log(line);
147323
+ if (hidden > 0)
147324
+ console.log(source_default.dim(` ${hiddenNote(hidden)}`));
147325
+ console.log();
147326
+ };
147047
147327
  if (isNew) {
147048
147328
  const lines = newContent.split("\n");
147049
147329
  if (lines[lines.length - 1] === "")
147050
147330
  lines.pop();
147051
- console.log(source_default.cyan(`@@ -0,0 +1,${lines.length} @@`));
147052
- for (const line of lines) {
147053
- console.log(source_default.green(`+${line}`));
147054
- }
147055
- console.log();
147331
+ emit(
147332
+ ["", source_default.bold(`--- ${from}`), source_default.bold(`+++ ${to}`), source_default.cyan(`@@ -0,0 +1,${lines.length} @@`)],
147333
+ lines.map((line) => source_default.green(`+${line}`))
147334
+ );
147056
147335
  return;
147057
147336
  }
147058
147337
  const patch = structuredPatch(
@@ -147069,20 +147348,21 @@ function showDiff(filePath2, oldContent, newContent) {
147069
147348
  console.log();
147070
147349
  return;
147071
147350
  }
147351
+ const body = [];
147072
147352
  for (const hunk of patch.hunks) {
147073
147353
  const hunkHeader = `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`;
147074
- console.log(source_default.cyan(hunkHeader));
147354
+ body.push(source_default.cyan(hunkHeader));
147075
147355
  for (const line of hunk.lines) {
147076
147356
  if (line.startsWith("+")) {
147077
- console.log(source_default.green(line));
147357
+ body.push(source_default.green(line));
147078
147358
  } else if (line.startsWith("-")) {
147079
- console.log(source_default.red(line));
147359
+ body.push(source_default.red(line));
147080
147360
  } else {
147081
- console.log(source_default.gray(line));
147361
+ body.push(source_default.gray(line));
147082
147362
  }
147083
147363
  }
147084
147364
  }
147085
- console.log();
147365
+ emit(["", source_default.bold(`--- ${from}`), source_default.bold(`+++ ${to}`)], body);
147086
147366
  }
147087
147367
 
147088
147368
  // src/permissions/risk.ts
@@ -147211,6 +147491,15 @@ function announceAgent(req) {
147211
147491
  console.error();
147212
147492
  console.error(source_default.cyan(` \u21B3 sub-agent ${req.agent.name}${req.agent.description ? ` (${req.agent.description})` : ""} asks:`));
147213
147493
  }
147494
+ function printCommandBox(command) {
147495
+ const lines = command.split("\n");
147496
+ const { show, hidden } = previewCap(lines.length);
147497
+ for (const line of lines.slice(0, show)) {
147498
+ console.error(source_default.yellow(" \u2502 ") + source_default.white(line));
147499
+ }
147500
+ if (hidden > 0)
147501
+ console.error(source_default.yellow(" \u2502 ") + source_default.dim(hiddenNote(hidden)));
147502
+ }
147214
147503
  async function requestPermission(req) {
147215
147504
  const { tool, input } = req;
147216
147505
  const decision = (0, import_code_core4.evaluatePermission)(_rules, tool, input, _workDir);
@@ -147227,9 +147516,7 @@ async function requestPermission(req) {
147227
147516
  console.error(source_default.red.bold(" \u26A0 DESTRUCTIVE / IRREVERSIBLE COMMAND"));
147228
147517
  console.error(source_default.red(` ${destructive.reason} [${destructive.category}]`));
147229
147518
  console.error(source_default.yellow(" \u250C\u2500 bash \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147230
- for (const line of command.split("\n")) {
147231
- console.error(source_default.yellow(" \u2502 ") + source_default.white(line));
147232
- }
147519
+ printCommandBox(command);
147233
147520
  console.error(source_default.yellow(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147234
147521
  console.error(source_default.dim(" This cannot be undone. Auto-approve does not apply here."));
147235
147522
  console.error();
@@ -147341,9 +147628,7 @@ async function askFlow(req) {
147341
147628
  const command = input.command ?? input.cmd ?? "";
147342
147629
  console.error();
147343
147630
  console.error(source_default.yellow(" \u250C\u2500 bash \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147344
- for (const line of command.split("\n")) {
147345
- console.error(source_default.yellow(" \u2502 ") + source_default.white(line));
147346
- }
147631
+ printCommandBox(command);
147347
147632
  console.error(source_default.yellow(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
147348
147633
  console.error();
147349
147634
  const v = await choose({
@@ -147454,6 +147739,9 @@ function redact(text, max = 400) {
147454
147739
  s2 = s2.replace(/(authorization["']?\s*[:=]\s*["']?)(bearer\s+)?[^\s"',}]+/gi, "$1[redacted]").replace(/\bbearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, "Bearer [redacted]").replace(/\b(sk|pk|rk|ghp|gho|ghs|github_pat|xox[abprs]|AKIA|AIza)[-_A-Za-z0-9]{12,}/g, "[redacted]").replace(/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}/g, "[redacted-jwt]").replace(/((?:api[_-]?key|token|secret|password|passwd)["']?\s*[:=]\s*["']?)[^\s"',}&]{4,}/gi, "$1[redacted]");
147455
147740
  return s2.length > max ? s2.slice(0, max) + "\u2026" : s2;
147456
147741
  }
147742
+ function debugLogPath() {
147743
+ return filePath;
147744
+ }
147457
147745
  function enableDebug(explicitPath) {
147458
147746
  try {
147459
147747
  const p = explicitPath ? path3.resolve(explicitPath) : path3.join(debugDir(), `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${process.pid}.log`);
@@ -154372,10 +154660,10 @@ var Output = class {
154372
154660
  transformers
154373
154661
  });
154374
154662
  }
154375
- clip(clip3) {
154663
+ clip(clip4) {
154376
154664
  this.operations.push({
154377
154665
  type: "clip",
154378
- clip: clip3
154666
+ clip: clip4
154379
154667
  });
154380
154668
  }
154381
154669
  unclip() {
@@ -154409,40 +154697,40 @@ var Output = class {
154409
154697
  const { text, transformers } = operation;
154410
154698
  let { x: x2, y } = operation;
154411
154699
  let lines = text.split("\n");
154412
- const clip3 = clips.at(-1);
154413
- if (clip3) {
154414
- const clipHorizontally = typeof clip3?.x1 === "number" && typeof clip3?.x2 === "number";
154415
- const clipVertically = typeof clip3?.y1 === "number" && typeof clip3?.y2 === "number";
154700
+ const clip4 = clips.at(-1);
154701
+ if (clip4) {
154702
+ const clipHorizontally = typeof clip4?.x1 === "number" && typeof clip4?.x2 === "number";
154703
+ const clipVertically = typeof clip4?.y1 === "number" && typeof clip4?.y2 === "number";
154416
154704
  if (clipHorizontally) {
154417
154705
  const width = this.caches.getWidestLine(text);
154418
- if (x2 + width < clip3.x1 || x2 > clip3.x2) {
154706
+ if (x2 + width < clip4.x1 || x2 > clip4.x2) {
154419
154707
  continue;
154420
154708
  }
154421
154709
  }
154422
154710
  if (clipVertically) {
154423
154711
  const height = lines.length;
154424
- if (y + height < clip3.y1 || y > clip3.y2) {
154712
+ if (y + height < clip4.y1 || y > clip4.y2) {
154425
154713
  continue;
154426
154714
  }
154427
154715
  }
154428
154716
  if (clipHorizontally) {
154429
154717
  lines = lines.map((line) => {
154430
- const from = x2 < clip3.x1 ? clip3.x1 - x2 : 0;
154718
+ const from = x2 < clip4.x1 ? clip4.x1 - x2 : 0;
154431
154719
  const width = this.caches.getStringWidth(line);
154432
- const to = x2 + width > clip3.x2 ? clip3.x2 - x2 : width;
154720
+ const to = x2 + width > clip4.x2 ? clip4.x2 - x2 : width;
154433
154721
  return sliceAnsi(line, from, to);
154434
154722
  });
154435
- if (x2 < clip3.x1) {
154436
- x2 = clip3.x1;
154723
+ if (x2 < clip4.x1) {
154724
+ x2 = clip4.x1;
154437
154725
  }
154438
154726
  }
154439
154727
  if (clipVertically) {
154440
- const from = y < clip3.y1 ? clip3.y1 - y : 0;
154728
+ const from = y < clip4.y1 ? clip4.y1 - y : 0;
154441
154729
  const height = lines.length;
154442
- const to = y + height > clip3.y2 ? clip3.y2 - y : height;
154730
+ const to = y + height > clip4.y2 ? clip4.y2 - y : height;
154443
154731
  lines = lines.slice(from, to);
154444
- if (y < clip3.y1) {
154445
- y = clip3.y1;
154732
+ if (y < clip4.y1) {
154733
+ y = clip4.y1;
154446
154734
  }
154447
154735
  }
154448
154736
  }
@@ -157611,7 +157899,7 @@ function ctxSegment(pct) {
157611
157899
  function money2(usd) {
157612
157900
  return usd > 0 && usd < 0.01 ? "$" + usd.toFixed(4) : "$" + usd.toFixed(2);
157613
157901
  }
157614
- function clip(s2, max) {
157902
+ function clip2(s2, max) {
157615
157903
  return s2.length <= max ? s2 : s2.slice(0, Math.max(1, max - 1)) + "\u2026";
157616
157904
  }
157617
157905
  function composeFooter(base, extras, columns, reserve = 0) {
@@ -157622,9 +157910,9 @@ function composeFooter(base, extras, columns, reserve = 0) {
157622
157910
  ];
157623
157911
  const optional = [];
157624
157912
  if (extras?.model)
157625
- optional.push({ keep: 3, at: 0, segment: { text: clip(extras.model, 24), tone: "dim" } });
157913
+ optional.push({ keep: 3, at: 0, segment: { text: clip2(extras.model, 24), tone: "dim" } });
157626
157914
  if (extras?.branch)
157627
- optional.push({ keep: 1, at: 1, segment: { text: "\u2387 " + clip(extras.branch, 24), tone: "dim" } });
157915
+ optional.push({ keep: 1, at: 1, segment: { text: "\u2387 " + clip2(extras.branch, 24), tone: "dim" } });
157628
157916
  if (typeof extras?.ctxPct === "number")
157629
157917
  optional.push({ keep: 4, at: 2, segment: ctxSegment(extras.ctxPct) });
157630
157918
  if (typeof extras?.costUsd === "number")
@@ -159672,7 +159960,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
159672
159960
  // src/ui/rewindPicker.ts
159673
159961
  var MAX_REWIND_CHOICES = 9;
159674
159962
  var LABEL_MAX = 52;
159675
- function clip2(text, max) {
159963
+ function clip3(text, max) {
159676
159964
  const t2 = text.replace(/\s+/g, " ").trim();
159677
159965
  return t2.length <= max ? t2 : t2.slice(0, max - 1) + "\u2026";
159678
159966
  }
@@ -159683,7 +159971,7 @@ function rewindChoices(cps) {
159683
159971
  const options = newest.map((c, i2) => {
159684
159972
  const files = `${c.fileCount} file${c.fileCount === 1 ? "" : "s"}`;
159685
159973
  const bash = c.bashCount > 0 ? ` \xB7 ${c.bashCount} shell cmd${c.bashCount === 1 ? "" : "s"} (not undone)` : "";
159686
- return { key: String(i2 + 1), label: `${clip2(c.label, LABEL_MAX)} (${files}${bash})` };
159974
+ return { key: String(i2 + 1), label: `${clip3(c.label, LABEL_MAX)} (${files}${bash})` };
159687
159975
  });
159688
159976
  options.push({ key: "n", label: "Cancel" });
159689
159977
  return { options, ids: newest.map((c) => c.id) };
@@ -160501,8 +160789,38 @@ function formatAgentsList(workDir) {
160501
160789
  }
160502
160790
  return lines.join("\n");
160503
160791
  }
160792
+ var turnStats = freshTurnStats();
160793
+ function printTurnSummary(outcome) {
160794
+ if (turnStats.endedAt <= turnStats.startedAt)
160795
+ turnStats.endedAt = Date.now();
160796
+ const lines = summaryLines2(turnStats, { outcome, cols: process.stdout.columns });
160797
+ const glyph = outcome === "ok" ? source_default.green("\u2713") : outcome === "interrupted" ? source_default.yellow("\u25E6") : source_default.red("\u2717");
160798
+ console.log(" " + glyph + " " + source_default.dim(lines[0]));
160799
+ for (const extra of lines.slice(1))
160800
+ console.log(" " + source_default.dim(extra));
160801
+ }
160802
+ function errorReportLines(err, aborted) {
160803
+ const view = describeError(err, { aborted, debugLogPath: debugLogPath() ?? void 0 });
160804
+ if (view.kind === "abort")
160805
+ return ["", source_default.yellow(" \u25E6 Interrupted")];
160806
+ const lines = ["", source_default.red(" \u2717 ") + source_default.bold(view.title)];
160807
+ const detail = errorDetail(err, view);
160808
+ if (detail)
160809
+ lines.push(source_default.dim(" " + detail));
160810
+ for (const h2 of view.hints)
160811
+ lines.push(source_default.dim(" " + h2));
160812
+ return lines;
160813
+ }
160814
+ function printErrorReport(err, aborted) {
160815
+ for (const l2 of errorReportLines(err, aborted))
160816
+ console.error(l2);
160817
+ }
160504
160818
  async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrallMd, mode, effort, checkpointManager, onProgress, takePendingInput, worktreeState, peerCtx) {
160505
160819
  let lastUsage;
160820
+ turnStats = freshTurnStats();
160821
+ const turnCostStart = sessionUsage.cost;
160822
+ const turnTokenStart = sessionUsage.totalTokens;
160823
+ const turnCachedStart = sessionUsage.cacheRead;
160506
160824
  const spinner = new Spinner();
160507
160825
  const mdRender = new MarkdownStreamRenderer();
160508
160826
  const toolStream = new ToolStreamPrinter(PREVIEW_LINES);
@@ -160614,7 +160932,13 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
160614
160932
  mdRender.flush();
160615
160933
  spinner.stop();
160616
160934
  if (meta?.id)
160617
- toolCalls.set(meta.id, { name, start: Date.now(), label: toolLabel(name, input) });
160935
+ toolCalls.set(meta.id, {
160936
+ name,
160937
+ start: Date.now(),
160938
+ label: toolLabel(name, input),
160939
+ command: name === "bash" && typeof input.command === "string" ? input.command : void 0
160940
+ });
160941
+ noteToolUse(turnStats, name, input, isSubTask);
160618
160942
  debugLog("tool_use", { tool: name, sub: !!isSubTask });
160619
160943
  if (isSubTask) {
160620
160944
  console.log(subPrefix(meta) + formatToolUse(name, input).trim());
@@ -160647,6 +160971,8 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
160647
160971
  const tracked = meta?.id ? toolCalls.get(meta.id) : void 0;
160648
160972
  if (meta?.id)
160649
160973
  toolCalls.delete(meta.id);
160974
+ if (!isSubTask && tracked?.command)
160975
+ noteBashResult(turnStats, tracked.command, res.error === void 0);
160650
160976
  debugLog("tool_result", { tool: tracked?.name ?? name, ms: tracked ? Date.now() - tracked.start : void 0, ok: res.error === void 0, error: res.error, sub: !!isSubTask });
160651
160977
  if (isSubTask) {
160652
160978
  const ms = tracked ? Date.now() - tracked.start : 0;
@@ -160690,6 +161016,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
160690
161016
  debugLog("usage", { input: u.input_tokens, output: u.output_tokens, cache_read: u.cache_read_input_tokens, cache_write: u.cache_creation_input_tokens, cost, partial: !!partial, sub: !!isSubTask });
160691
161017
  },
160692
161018
  onApiCallDuration: (ms) => {
161019
+ turnStats.rounds++;
160693
161020
  debugLog("api_call", { ms });
160694
161021
  },
160695
161022
  // A transient disconnect (network drop, machine sleep/wake, overloaded upstream)
@@ -160731,6 +161058,12 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
160731
161058
  mdRender.flush();
160732
161059
  printBalanceNotice(balance, zero);
160733
161060
  },
161061
+ // The user's Esc / Ctrl+C. Wiring this into the loop is what makes "interrupt" mean
161062
+ // STOP rather than "stop drawing": without it the signal only silenced the event
161063
+ // handlers below, and the agent kept calling tools and the API (and spending money)
161064
+ // until it happened to finish — measured live: Esc at 12s left `sleep 30` running to
161065
+ // 30.0s and the turn still made its next request. Headless mode already passed it.
161066
+ abortSignal,
160734
161067
  requestPermission,
160735
161068
  checkpointManager,
160736
161069
  onProgress,
@@ -160745,6 +161078,11 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
160745
161078
  if (lastUsage) {
160746
161079
  console.log(source_default.dim(" " + formatUsage(lastUsage.output_tokens)));
160747
161080
  }
161081
+ turnStats.tokens = Math.max(0, sessionUsage.totalTokens - turnTokenStart);
161082
+ turnStats.cachedTokens = Math.max(0, sessionUsage.cacheRead - turnCachedStart);
161083
+ turnStats.costKnown = sessionUsage.costReported;
161084
+ turnStats.costUsd = Math.max(0, sessionUsage.cost - turnCostStart);
161085
+ printTurnSummary(abortSignal.aborted ? "interrupted" : "ok");
160748
161086
  return { messages: result, usage: lastUsage };
160749
161087
  }
160750
161088
  function toolIds(isSubTask, meta) {
@@ -161243,7 +161581,8 @@ Resume it with: nex --worktree ${worktreeState.name}
161243
161581
  };
161244
161582
  const recoverTurn = (err, current) => {
161245
161583
  const salvaged = (0, import_code_core8.salvageHistory)(err);
161246
- console.error("\n" + source_default.red("Error: ") + String(err.message));
161584
+ printErrorReport(err, abortSignal.aborted);
161585
+ printTurnSummary(abortSignal.aborted ? "interrupted" : "failed");
161247
161586
  if (!salvaged)
161248
161587
  return current;
161249
161588
  const rounds = err.progressCount ?? 1;
@@ -161321,7 +161660,8 @@ ${text}` : "");
161321
161660
  ...salvaged ? { resumable: true, messagesCompleted: salvaged.length } : {}
161322
161661
  }) + "\n");
161323
161662
  } else {
161324
- console.error(source_default.red("\nError: ") + String(err.message));
161663
+ for (const l2 of errorReportLines(err, abortSignal2.aborted))
161664
+ console.error(l2);
161325
161665
  if (salvaged) {
161326
161666
  console.error(source_default.dim(" Progress was saved \u2014 run `nex --resume` (or `nex -r`) to resume this session."));
161327
161667
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.133",
3
+ "version": "0.5.134",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",