@nexrall/code-core 1.4.40 → 1.4.42

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.
@@ -70,7 +70,7 @@ export declare function executeAgentMemoryWrite(input: Record<string, unknown>,
70
70
  * final answer, or the user pressed Ctrl+C and already knows why it stopped.
71
71
  * EVERYTHING else owes the user an explanation, which is what stopReasonNotice covers.
72
72
  */
73
- export type StopReason = 'clean' | 'aborted' | 'reported-elsewhere' | 'empty-response' | 'no-balance' | 'stalled' | 'stalled-repeat' | 'budget' | 'unknown';
73
+ export type StopReason = 'clean' | 'aborted' | 'reported-elsewhere' | 'empty-response' | 'output-limit' | 'no-balance' | 'stalled' | 'stalled-repeat' | 'budget' | 'unknown';
74
74
  /**
75
75
  * The message shown when a run ends for any reason other than a clean finish.
76
76
  *
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
@@ -331,6 +331,17 @@ function stopReasonNotice(reason, ctx = {}) {
331
331
  case 'empty-response':
332
332
  return `\n\u26a0\ufe0f The model returned an empty response, so nothing was done. This is usually a transient ` +
333
333
  `upstream hiccup \u2014 send "continue" to retry.\n`;
334
+ // Deliberately NOT folded into 'empty-response'. Both arrive as an assistant
335
+ // message with no content once thinking blocks are stripped, so they used to
336
+ // be indistinguishable — and the user was told the truncation case was "a
337
+ // transient upstream hiccup" they should retry with "continue". Both halves
338
+ // were wrong: nothing was transient, and continuing re-runs the same prompt
339
+ // with the same budget and the same reasoning behaviour, reproducing it
340
+ // exactly. Naming the cause is the fix; the advice has to change with it.
341
+ case 'output-limit':
342
+ return `\n\u26a0\ufe0f The model used its entire output budget on internal reasoning and was cut off ` +
343
+ `before it could reply, so nothing was done. This will repeat identically if you just retry \u2014 ` +
344
+ `lower the reasoning effort (/effort) or split the task into smaller steps.\n`;
334
345
  case 'no-balance':
335
346
  return `\n\ud83d\udcb3 Stopped: your balance is empty, so the request was rejected before it started. ` +
336
347
  `Top up and send "continue" \u2014 no tokens were used for this turn.\n`;
@@ -2076,6 +2087,26 @@ function originalTaskText(messages) {
2076
2087
  .join('\n')
2077
2088
  .trim();
2078
2089
  }
2090
+ /**
2091
+ * The summariser's own model call failed — as opposed to running fine but not
2092
+ * shrinking anything.
2093
+ *
2094
+ * These two outcomes used to be one `return false`, and collapsing them was a
2095
+ * real bug: the in-loop circuit breaker disables auto-compaction permanently
2096
+ * after COMPACT_MAX_FAILURES, on the sound theory that a compaction which cannot
2097
+ * reclaim bytes will never start. But a summariser that THREW says nothing about
2098
+ * whether compaction would help — only that the network/provider was unavailable
2099
+ * for a moment. Feeding those into the same counter meant three transient blips
2100
+ * (a 529 burst, a brief outage, a rate-limit spike) permanently switched off the
2101
+ * one mechanism keeping the context under control, and the run then died at the
2102
+ * context wall minutes later with its own recovery already disabled.
2103
+ */
2104
+ class CompactionUnavailableError extends Error {
2105
+ constructor() {
2106
+ super('Compaction summariser was unavailable');
2107
+ this.name = 'CompactionUnavailableError';
2108
+ }
2109
+ }
2079
2110
  async function autoCompactMessages(messages, options, ledger) {
2080
2111
  const cut = findSafeCutIndex(messages, messages.length - COMPACT_KEEP_MIN);
2081
2112
  if (cut < 2)
@@ -2124,7 +2155,13 @@ async function autoCompactMessages(messages, options, ledger) {
2124
2155
  .trim();
2125
2156
  }
2126
2157
  catch {
2127
- return false; // summarisation failedleave history as is, the turn may still fit
2158
+ // Summarisation FAILEDthe model call itself threw (network blip, 529,
2159
+ // provider quota). Distinguished from "ran fine but didn't help" by the
2160
+ // caller, because the two must not feed the same circuit breaker: three
2161
+ // transient network errors would otherwise permanently disable compaction
2162
+ // for the rest of the run, leaving the context to grow until the turn dies
2163
+ // with no recovery left. Leave history as is; the turn may still fit.
2164
+ throw new CompactionUnavailableError();
2128
2165
  }
2129
2166
  if (!summary)
2130
2167
  return false;
@@ -2264,17 +2301,30 @@ async function compactMessagesForResume(messages, opts) {
2264
2301
  // No ledger at resume time — the ledger is per-run, in-memory, and would
2265
2302
  // have been created fresh anyway since this is a new process/run. The
2266
2303
  // ORIGINAL TASK verbatim pin (inside autoCompactMessages) still applies.
2267
- const did = await autoCompactMessages(messages, {
2268
- workDir: opts.workDir,
2269
- model: opts.model,
2270
- clientType: opts.clientType,
2271
- env: opts.env,
2272
- onText: () => { },
2273
- onToolUse: () => { },
2274
- onToolResult: () => { },
2275
- onUsage: () => { },
2276
- requestPermission: async () => false,
2277
- });
2304
+ // A summariser failure is not fatal HERE. This runs before the session is
2305
+ // handed back to the user, so the worst case is resuming with a longer (more
2306
+ // expensive) prefix — strictly better than refusing to resume at all. The
2307
+ // in-loop caller treats the same signal differently, because there it must
2308
+ // decide whether to arm a circuit breaker.
2309
+ let did;
2310
+ try {
2311
+ did = await autoCompactMessages(messages, {
2312
+ workDir: opts.workDir,
2313
+ model: opts.model,
2314
+ clientType: opts.clientType,
2315
+ env: opts.env,
2316
+ onText: () => { },
2317
+ onToolUse: () => { },
2318
+ onToolResult: () => { },
2319
+ onUsage: () => { },
2320
+ requestPermission: async () => false,
2321
+ });
2322
+ }
2323
+ catch (err) {
2324
+ if (err instanceof CompactionUnavailableError)
2325
+ break;
2326
+ throw err;
2327
+ }
2278
2328
  if (!did)
2279
2329
  break;
2280
2330
  compacted = true;
@@ -2430,6 +2480,19 @@ async function runAgentLoop(initialMessages, options) {
2430
2480
  // the in-loop path in line with it.
2431
2481
  let compactFailures = 0;
2432
2482
  let compactDisabled = false;
2483
+ // Body size at the moment the breaker tripped, so it can be RE-ARMED if the
2484
+ // conversation later grows substantially past that point.
2485
+ //
2486
+ // Without this the breaker is a one-way latch for the whole run: "compaction
2487
+ // couldn't shrink THIS history" silently becomes "compaction will never help
2488
+ // again", even after several more turns of tool output have been appended —
2489
+ // a materially different history, and one where summarising may well work.
2490
+ // The run would instead sail into the context wall with its own safety net
2491
+ // switched off. Requiring real growth (not just "still over threshold")
2492
+ // is what keeps this from re-entering the useless-retry loop the breaker
2493
+ // exists to stop.
2494
+ let compactDisabledAtBytes = 0;
2495
+ const COMPACT_REARM_GROWTH = 1.5; // 50% larger than when we gave up
2433
2496
  try {
2434
2497
  for (; iteration < budget; iteration++) {
2435
2498
  if (options.abortSignal?.aborted) {
@@ -2480,6 +2543,12 @@ async function runAgentLoop(initialMessages, options) {
2480
2543
  (options.onNotice ?? options.onText)(`\u267b\ufe0f Trimmed ~${(reclaimed / (1024 * 1024)).toFixed(1)}MB of already-processed tool output to keep this chat cheap to continue.`);
2481
2544
  }
2482
2545
  }
2546
+ // Re-arm the breaker once the conversation has grown materially past the
2547
+ // size at which compaction was given up on. See compactDisabledAtBytes.
2548
+ if (compactDisabled && bodyBytes > compactDisabledAtBytes * COMPACT_REARM_GROWTH) {
2549
+ compactDisabled = false;
2550
+ compactFailures = 0;
2551
+ }
2483
2552
  if (autoCompact && !compactDisabled && !compacting && (tokenPressure || bytePressure) && messages.length > COMPACT_KEEP_MIN + 2) {
2484
2553
  compacting = true;
2485
2554
  try {
@@ -2488,7 +2557,19 @@ async function runAgentLoop(initialMessages, options) {
2488
2557
  // reclaims nothing is a failure for our purposes — it leaves the trigger
2489
2558
  // armed for the next iteration, which is precisely the runaway.
2490
2559
  const bytesBefore = estimateBodyBytes(messages);
2491
- const did = await autoCompactMessages(messages, options, ledger);
2560
+ // Separated from `did` because they answer different questions: `did`
2561
+ // is "was history rewritten", `unavailable` is "did we even get to
2562
+ // find out". Only the former may feed the circuit breaker.
2563
+ let did = false;
2564
+ let unavailable = false;
2565
+ try {
2566
+ did = await autoCompactMessages(messages, options, ledger);
2567
+ }
2568
+ catch (err) {
2569
+ if (!(err instanceof CompactionUnavailableError))
2570
+ throw err;
2571
+ unavailable = true;
2572
+ }
2492
2573
  const bytesAfter = did ? estimateBodyBytes(messages) : bytesBefore;
2493
2574
  const reclaimed = bytesBefore - bytesAfter;
2494
2575
  if (did) {
@@ -2506,17 +2587,28 @@ async function runAgentLoop(initialMessages, options) {
2506
2587
  // Productive == it shrank the body meaningfully. A successful-but-useless
2507
2588
  // compaction counts as a failure, otherwise the "cannot get under the byte
2508
2589
  // cap" case would never trip the breaker.
2509
- if (did && reclaimed >= COMPACT_MIN_RECLAIM_BYTES) {
2590
+ //
2591
+ // `unavailable` is excluded entirely — not counted as a failure AND not
2592
+ // counted as a success. The breaker exists to stop re-summarising a
2593
+ // conversation that provably cannot shrink; a summariser that never ran
2594
+ // is no evidence either way, and treating it as failure is what used to
2595
+ // let three network blips permanently disable compaction mid-run.
2596
+ if (unavailable) {
2597
+ // Nothing to record. Pressure is unchanged, so the next iteration
2598
+ // retries — which is the correct response to a transient outage.
2599
+ }
2600
+ else if (did && reclaimed >= COMPACT_MIN_RECLAIM_BYTES) {
2510
2601
  compactFailures = 0;
2511
2602
  }
2512
2603
  else if (++compactFailures >= COMPACT_MAX_FAILURES) {
2513
2604
  compactDisabled = true;
2605
+ compactDisabledAtBytes = bytesAfter;
2514
2606
  // Surfaced ONCE. The user needs to know the automatic safety net is off
2515
2607
  // (so a context-window error later isn't a total surprise) and what to do
2516
2608
  // about it, but repeating this every iteration would be its own spam.
2517
- (options.onNotice ?? options.onText)(`\u26a0\ufe0f Auto-compaction isn't reducing this conversation any further, so it's been switched off ` +
2518
- `for the rest of this run to avoid repeated summarising. If the context fills up, start a fresh ` +
2519
- `chat or run /compact manually.`);
2609
+ (options.onNotice ?? options.onText)(`\u26a0\ufe0f Auto-compaction isn't reducing this conversation any further, so it's paused to avoid ` +
2610
+ `repeated summarising. It will be retried automatically if the conversation grows substantially. ` +
2611
+ `If the context fills up before then, start a fresh chat or run /compact manually.`);
2520
2612
  }
2521
2613
  }
2522
2614
  finally {
@@ -2637,6 +2729,17 @@ async function runAgentLoop(initialMessages, options) {
2637
2729
  ? options._allowedTools.has('memory_write')
2638
2730
  : true, // no allowlist = main agent = may write
2639
2731
  hasOwnAgentStore: !!options._agentMemory,
2732
+ // Same derivation, same reason, for the network. The base prompt's
2733
+ // "Web search" section tells the run to reach the network, but
2734
+ // READ_ONLY_TOOLS (explorer, security-auditor, ...) contains neither
2735
+ // `fetch_url` nor `web_search`. MEASURED on the 195-trial 2026-08-10
2736
+ // deepseek-v4-pro benchmark: 29 of 51 `fetch_url` failures and 3 of 14
2737
+ // `web_search` failures were the permission gate refusing a sub-agent
2738
+ // that its own system prompt had just invited to browse — every one a
2739
+ // wasted round-trip on a paid turn.
2740
+ canBrowseWeb: options._allowedTools
2741
+ ? options._allowedTools.has('fetch_url')
2742
+ : true, // no allowlist = main agent = may browse
2640
2743
  // Withholds the `task` schema and its instructions when this run cannot
2641
2744
  // delegate — see canSpawnSubAgents.
2642
2745
  canSpawnSubAgents: maySpawn,
@@ -2706,8 +2809,18 @@ async function runAgentLoop(initialMessages, options) {
2706
2809
  // caller the task succeeded when the model had in fact produced nothing at all
2707
2810
  // — indistinguishable, to the user, from a finished job with no summary. An
2708
2811
  // empty turn is a (usually transient) upstream failure, so name it and say so.
2812
+ //
2813
+ // …but only ONE of the two ways to get here is transient. `stopReason` must be
2814
+ // consulted RIGHT HERE, before breaking: it is read again further down for the
2815
+ // truncated-tool-call guard, but that code is unreachable from this branch, so
2816
+ // this used to report every truncation as a transient "empty response" and tell
2817
+ // the user to send "continue" — advice that reproduces the failure verbatim.
2818
+ //
2819
+ // 'max_tokens' means the model was CUT OFF at its output ceiling. Landing here
2820
+ // with empty content means the entire budget went to thinking blocks that were
2821
+ // just stripped, leaving nothing to show. Deterministic, not a hiccup.
2709
2822
  runSimpleHooks(hooks.PostMessageComplete, options.workDir);
2710
- stopReason = 'empty-response';
2823
+ stopReason = assistantMessage.stopReason === 'max_tokens' ? 'output-limit' : 'empty-response';
2711
2824
  break;
2712
2825
  }
2713
2826
  // stopReason is transient, single-turn metadata for the truncated-tool-call guard
@@ -66,6 +66,17 @@ export interface StreamChatOptions {
66
66
  * "main agent" (may spawn).
67
67
  */
68
68
  canSpawnSubAgents?: boolean;
69
+ /**
70
+ * Whether THIS run may reach the network, i.e. whether the backend should render the
71
+ * prompt's "Web search" section at all.
72
+ *
73
+ * Same rule as canWriteSharedMemory / canSpawnSubAgents, applied to the network:
74
+ * READ_ONLY_TOOLS grants neither `fetch_url` nor `web_search`, yet the base prompt
75
+ * told every run to search the web, so read-only sub-agents (explorer,
76
+ * security-auditor, ...) kept calling a tool their own allowlist then refused.
77
+ * Undefined means "main agent" (may browse).
78
+ */
79
+ canBrowseWeb?: boolean;
69
80
  /**
70
81
  * Opt in to restarting a turn that died AFTER partial output was rendered.
71
82
  *
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAA8B,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAiErH,eAAO,MAAM,QAAQ,QAAmB,CAAC;AAIzC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACtD,CAAC,EAAE,CAeL;AAiGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,oFAAoF;IACpF,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACjG,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6IAA6I;IAC7I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;;;;;OAeG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAiDD,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAwlClB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9D;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYxD;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CA0BlD;AAID,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B1E;AAID,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BhF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAA8B,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAiErH,eAAO,MAAM,QAAQ,QAAmB,CAAC;AAIzC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACtD,CAAC,EAAE,CAeL;AAiGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,oFAAoF;IACpF,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACjG,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6IAA6I;IAC7I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;;;;;;;;OAeG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA8ID,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAimClB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9D;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYxD;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CA0BlD;AAID,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+B1E;AAID,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA0BhF"}
@@ -226,6 +226,89 @@ const MAX_TOTAL_RETRY_MS = (() => {
226
226
  const raw = Number(process.env.NEXRALL_MAX_RETRY_MS);
227
227
  return Number.isFinite(raw) && raw > 0 ? raw : 5 * 60000;
228
228
  })();
229
+ /**
230
+ * Ceiling on how long `fetch()` may sit waiting for RESPONSE HEADERS.
231
+ *
232
+ * This closes a genuine hang, observed in production: a Terminal-Bench trial went
233
+ * silent 38 s in and stayed silent for 15 minutes until the harness killed it,
234
+ * with ZERO retry events emitted — proof no watchdog ever fired.
235
+ *
236
+ * The reason none could: every watchdog in this file lives inside the stream-
237
+ * reading promise, and that promise is only constructed once `response.body`
238
+ * exists. `await fetch(...)` resolves when headers arrive, so the window between
239
+ * "socket opened" and "headers received" is guarded by NOTHING. A server that
240
+ * accepts the TCP connection and then never replies (a wedged proxy, a pod
241
+ * mid-eviction, a silently dropped connection where no RST is delivered) parks
242
+ * that await forever. `signal` alone does not help: it only carries a manual
243
+ * abort, and there is no human present in a headless run.
244
+ *
245
+ * 120 s is deliberately generous relative to time-to-first-token — it does NOT
246
+ * bound generation. The backend calls res.flushHeaders() before it ever touches
247
+ * the model (routes/code.js), so headers are due within a network round trip
248
+ * plus request validation, no matter how large the prompt or how long the model
249
+ * subsequently thinks. Anything past two minutes here is a dead connection, not
250
+ * a slow one.
251
+ *
252
+ * Read at CALL time, not module-load time. Every other tunable in this file that
253
+ * tests need to reach (NEXRALL_PROGRESS_TIMEOUT_MS, NEXRALL_FIRST_EVENT_TIMEOUT_MS)
254
+ * is read inside the function that uses it for the same reason: this module is
255
+ * imported once per process, so a load-time constant would freeze whatever the
256
+ * environment happened to be at import and silently ignore any later override —
257
+ * making the timeout path untestable and env configuration a no-op for anything
258
+ * that sets it after startup.
259
+ */
260
+ const headerTimeoutMs = () => Number(process.env.NEXRALL_HEADER_TIMEOUT_MS) || 120000;
261
+ /**
262
+ * `fetch` with a bound on the header-wait phase ONLY.
263
+ *
264
+ * The subtlety that makes this worth a helper: the timer MUST be cleared as soon
265
+ * as headers land. The returned body is consumed for minutes afterwards (that is
266
+ * the entire point of a streaming turn), and this signal stays attached to the
267
+ * live response — a timer left running would abort a perfectly healthy stream
268
+ * mid-generation at the two-minute mark. Streaming is instead policed by the
269
+ * heartbeat/progress watchdogs downstream, which understand SSE framing.
270
+ *
271
+ * The caller's own abort signal is chained in, so a user pressing Stop during
272
+ * the header wait still cancels immediately.
273
+ */
274
+ async function fetchWithHeaderTimeout(url, init, outerSignal, timeoutMs = headerTimeoutMs()) {
275
+ const headerController = new AbortController();
276
+ const onOuterAbort = () => headerController.abort();
277
+ if (outerSignal.aborted)
278
+ headerController.abort();
279
+ else
280
+ outerSignal.addEventListener('abort', onOuterAbort, { once: true });
281
+ let timedOut = false;
282
+ const timer = setTimeout(() => {
283
+ timedOut = true;
284
+ headerController.abort();
285
+ }, timeoutMs);
286
+ try {
287
+ return await (0, node_fetch_1.default)(url, { ...init, signal: headerController.signal });
288
+ }
289
+ catch (err) {
290
+ // Distinguish OUR timeout from the user's Stop. Without this the caller sees
291
+ // a bare AbortError and treats a dead server as a deliberate cancellation,
292
+ // which exits the run silently instead of retrying — the same class of
293
+ // "looks like the user did it" mistake the abort-vs-error split elsewhere
294
+ // in this file exists to prevent.
295
+ if (timedOut && !outerSignal.aborted) {
296
+ throw Object.assign(new Error(`The server accepted the connection but sent no response within ` +
297
+ `${Math.round(timeoutMs / 1000)} s. Reconnecting…`),
298
+ // Retryable by definition: nothing was received, so a fresh attempt
299
+ // cannot duplicate any rendered output.
300
+ { retryable: true });
301
+ }
302
+ throw err;
303
+ }
304
+ finally {
305
+ // Both of these are load-bearing, not hygiene. Leaving the timer armed would
306
+ // abort the live stream two minutes into a healthy turn; leaving the listener
307
+ // attached leaks one entry per attempt onto a signal that outlives them all.
308
+ clearTimeout(timer);
309
+ outerSignal.removeEventListener('abort', onOuterAbort);
310
+ }
311
+ }
229
312
  /** Exponential backoff with full jitter, capped at RETRY_MAX_MS. The jitter spreads
230
313
  * retries out so a fleet of clients hitting the SAME overloaded API (529) don't all
231
314
  * reconnect in lockstep and re-trigger the overload (thundering herd). Returns a delay
@@ -243,7 +326,7 @@ function sleep(ms) {
243
326
  * the nested product (36) exists to bound pathological cases, not to be reached. */
244
327
  const MAX_TOTAL_ATTEMPTS = (MAX_RETRIES + 1) * (MAX_RETRIES + 1);
245
328
  async function streamChat(messages, options, onEvent) {
246
- const { model, env, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents } = options;
329
+ const { model, env, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb } = options;
247
330
  // IDEMPOTENCY KEY for this logical turn, generated ONCE and reused by every retry
248
331
  // below (that reuse is the entire point — a fresh id per attempt would be useless).
249
332
  //
@@ -315,14 +398,19 @@ async function streamChat(messages, options, onEvent) {
315
398
  // of the one already generating server-side (GET /chat/resume + Last-Event-ID). The
316
399
  // resume carries no body at all — the conversation is already being processed; re-sending
317
400
  // it is exactly the waste this avoids.
401
+ // The `signal` set here is a FALLBACK: fetchWithHeaderTimeout overrides it with a
402
+ // chained one so the header wait can be timed out independently. It stays because
403
+ // this shape is also what an unwrapped fetch would need, and a request with no
404
+ // signal at all would silently become uncancellable if that wrapper were removed.
318
405
  const buildFetchArgs = () => {
406
+ const signal = controller.signal;
319
407
  if (resuming) {
320
408
  return [
321
409
  `${exports.API_BASE}/api/code/chat/resume?turnId=${encodeURIComponent(turnId)}`,
322
410
  {
323
411
  method: 'GET',
324
412
  headers: { ...authHeaders(), 'Last-Event-ID': String(lastEventId) },
325
- signal: controller.signal,
413
+ signal,
326
414
  },
327
415
  ];
328
416
  }
@@ -331,8 +419,8 @@ async function streamChat(messages, options, onEvent) {
331
419
  {
332
420
  method: 'POST',
333
421
  headers: authHeaders(),
334
- body: JSON.stringify({ messages, model, env, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents }),
335
- signal: controller.signal,
422
+ body: JSON.stringify({ messages, model, env, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb }),
423
+ signal,
336
424
  },
337
425
  ];
338
426
  };
@@ -482,7 +570,11 @@ async function streamChat(messages, options, onEvent) {
482
570
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
483
571
  try {
484
572
  errorBodyOverride = null; // a fresh response — never reuse the previous body
485
- response = await (0, node_fetch_1.default)(...buildFetchArgs());
573
+ // Header-phase timeout only — see fetchWithHeaderTimeout. It replaces the
574
+ // signal built here with its own (chained to controller.signal), because
575
+ // the timer must be able to abort the header wait without outliving that
576
+ // wait and killing the stream it was guarding.
577
+ response = await fetchWithHeaderTimeout(...buildFetchArgs(), controller.signal);
486
578
  if (response.status === 429 && attempt < MAX_RETRIES && canRetry()) {
487
579
  reportRetry('Rate limited by the API — retrying');
488
580
  const retryAfter = parseInt(response.headers.get('retry-after') ?? '0', 10);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexrall/code-core",
3
- "version": "1.4.40",
3
+ "version": "1.4.42",
4
4
  "description": "Core agent loop, tools, and extension primitives for Nexrall Code — embed an AI coding agent in any Node.js application.",
5
5
  "license": "MIT",
6
6
  "author": "Nexrall <support@nexrall.com> (https://nexrall.com)",