@nexus-cortex/core 4.124.36 → 4.124.38

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.
@@ -90,6 +90,7 @@ import { InitCortexContext, MemoryWrite, MemoryRecall } from '../tools/context-m
90
90
  import { pruneAgedToolResults } from '../conversation/ToolResultPruner.js';
91
91
  import { detectTailRepetition, tailLoopGuardEnabled } from './tailRepetitionDetector.js';
92
92
  import { resolveEffortRamp, rampEffortFor } from './effortRamp.js';
93
+ import { resolveWallDrop, resolveOutputCap, dropWalledTurn, resolveWallSummary, extractWalledReasoning, clipReasoning, formatWallSummary } from './wallGuard.js';
93
94
  import { resolveComputeNudge, computeNudgeDecision, buildComputeNudgeLine, lastAssistantReasoningTokens } from './computeNudge.js';
94
95
  import { classifyEmptyResponse, emptyResponseNudge, nudgeForbidsTools, emptyResponseNudgeFor, isReasoningExhaustion, stepDownEffort, effortLadderFor, resolveExhaustLadderMode, resolveReasoningExhaustBackoff } from './emptyResponseClassifier.js';
95
96
  import { isImageRejectionError, stripRejectedImages } from './imageRejectionHeal.js'; // R154
@@ -1707,6 +1708,7 @@ export class CortexOrchestrator {
1707
1708
  if (!this.sessionTimeline) {
1708
1709
  throw new Error('Session not initialized. Call createSession() first.');
1709
1710
  }
1711
+ this.applyOutputCap(options); // HB-OUTPUT-CAP (dark)
1710
1712
  // Item 10: full-mass pre-assembly boundary — doctrine curation completes
1711
1713
  // BEFORE the turn-0 prompt is built (under defer this no-ops here and the
1712
1714
  // lift boundary awaits instead; one-shot flag makes both calls safe).
@@ -2572,6 +2574,10 @@ export class CortexOrchestrator {
2572
2574
  console.warn(`[Orchestrator] Empty response detected (${emptyClass.kind}, hadReasoning=${emptyClass.hadReasoning}, iteration=${toolCallIteration}). ` +
2573
2575
  `Retrying once with explicit completion prompt.`);
2574
2576
  const exhaustionLevel = this.armReasoningBackoffIfExhausted(emptyClass, options.parameters?.reasoningEffort ?? effectiveModel?.reasoning?.effort, toolCallIteration, currentAssistantMessage?.usage?.outputTokens, effectiveModel.provider);
2577
+ // HB-WALL-DROP (CORTEX_WALL_DROP, dark): drop the walled turn + carry the nudge on the newest tool_result (cache-preserving retry).
2578
+ const wallSummary = await this.summarizeWall(emptyClass, toolCallIteration, false); // HB-WALL-SUMMARY (dark); '' when off/failed
2579
+ const wallNudge = `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}${wallSummary}</system-reminder>`;
2580
+ const wallDrop = this.applyWallDrop(emptyClass, wallNudge, toolCallIteration, false);
2575
2581
  // R26 (2026-05-15, surfaced by A/B benchmark): the empty assistant
2576
2582
  // turn is already in messageHistory. The retry below rebuilds the
2577
2583
  // full history and re-sends it. Anthropic's Messages API tolerates
@@ -2580,8 +2586,8 @@ export class CortexOrchestrator {
2580
2586
  // the entire retry. R18b was validated on Claude, so this was
2581
2587
  // invisible until a grok run emitted an empty turn. Repair the
2582
2588
  // empty assistant message in-place with a minimal placeholder so
2583
- // every provider's message-content contract is satisfied.
2584
- for (let i = this.messageHistory.length - 1; i >= 0; i--) {
2589
+ // every provider's message-content contract is satisfied. (Skipped when HB-WALL-DROP removed the turn.)
2590
+ for (let i = wallDrop.dropped ? -1 : this.messageHistory.length - 1; i >= 0; i--) {
2585
2591
  const m = this.messageHistory[i];
2586
2592
  const isAssistant = m?.type === 'assistant' || m?.message?.role === 'assistant';
2587
2593
  if (!isAssistant)
@@ -2610,7 +2616,7 @@ export class CortexOrchestrator {
2610
2616
  role: 'user',
2611
2617
  content: [{
2612
2618
  type: 'text',
2613
- text: `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}</system-reminder>`,
2619
+ text: wallNudge,
2614
2620
  }],
2615
2621
  },
2616
2622
  timeline: {
@@ -2624,8 +2630,10 @@ export class CortexOrchestrator {
2624
2630
  apiPattern: effectiveModel.api.pattern,
2625
2631
  },
2626
2632
  };
2627
- this.messageHistory.push(followupUserMessage);
2628
- await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
2633
+ if (wallDrop.carrier !== 'tool_result') { // HB-WALL-DROP carried the nudge on the newest tool_result already
2634
+ this.messageHistory.push(followupUserMessage);
2635
+ await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
2636
+ }
2629
2637
  // Build a continuation request and re-call. Mirrors the
2630
2638
  // tool-result continuation path (lines ~1395-1466) but without
2631
2639
  // tool_result blocks.
@@ -4130,6 +4138,7 @@ export class CortexOrchestrator {
4130
4138
  if (!this.currentSessionId || !this.sessionTimeline) {
4131
4139
  throw new Error('Session not initialized. Call createSession() first.');
4132
4140
  }
4141
+ this.applyOutputCap(options); // HB-OUTPUT-CAP (dark)
4133
4142
  // Graduation-signal capture (Decision Capture Layer §5) — same as sendMessage:
4134
4143
  // score the previous turn's prediction against THIS user message + record.
4135
4144
  if (this.pendingTurnPrediction && this.pendingTurnPrediction.sessionId === this.currentSessionId) {
@@ -4778,8 +4787,12 @@ export class CortexOrchestrator {
4778
4787
  console.warn(`[Orchestrator Streaming] R32/R18b: Empty response (${emptyClass.kind}, iteration=${toolCallIteration}). ` +
4779
4788
  `Retrying with tools preserved.`);
4780
4789
  const exhaustionLevel = this.armReasoningBackoffIfExhausted(emptyClass, options.parameters?.reasoningEffort ?? effectiveModel?.reasoning?.effort, toolCallIteration, this.messageHistory[this.messageHistory.length - 1]?.usage?.outputTokens, effectiveModel.provider);
4781
- // R26 repair: ensure the empty assistant turn has content (xAI hard-400s on empty)
4782
- for (let i = this.messageHistory.length - 1; i >= 0; i--) {
4790
+ // HB-WALL-DROP (streaming parity).
4791
+ const wallSummary = await this.summarizeWall(emptyClass, toolCallIteration, true);
4792
+ const wallNudge = `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}${wallSummary}</system-reminder>`;
4793
+ const wallDrop = this.applyWallDrop(emptyClass, wallNudge, toolCallIteration, true);
4794
+ // R26 repair: ensure the empty assistant turn has content (xAI hard-400s on empty). Skipped when HB-WALL-DROP removed it.
4795
+ for (let i = wallDrop.dropped ? -1 : this.messageHistory.length - 1; i >= 0; i--) {
4783
4796
  const m = this.messageHistory[i];
4784
4797
  const isAssistant = m?.type === 'assistant' || m?.message?.role === 'assistant';
4785
4798
  if (!isAssistant)
@@ -4802,7 +4815,7 @@ export class CortexOrchestrator {
4802
4815
  role: 'user',
4803
4816
  content: [{
4804
4817
  type: 'text',
4805
- text: `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}</system-reminder>`,
4818
+ text: wallNudge,
4806
4819
  }],
4807
4820
  },
4808
4821
  timeline: {
@@ -4816,8 +4829,10 @@ export class CortexOrchestrator {
4816
4829
  apiPattern: effectiveModel.api.pattern,
4817
4830
  },
4818
4831
  };
4819
- this.messageHistory.push(followupUserMessage);
4820
- await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
4832
+ if (wallDrop.carrier !== 'tool_result') {
4833
+ this.messageHistory.push(followupUserMessage);
4834
+ await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
4835
+ }
4821
4836
  try {
4822
4837
  await this.ensureHistoryFitsModel(effectiveModel);
4823
4838
  const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
@@ -9219,6 +9234,57 @@ export class CortexOrchestrator {
9219
9234
  return undefined;
9220
9235
  }
9221
9236
  }
9237
+ /** HB-WALL-SUMMARY (CORTEX_WALL_SUMMARY=on, dark): one helper call condensing the walled turn's reasoning; returns the attributed block for
9238
+ * the nudge, or '' (off / not a reasoning wall / no reasoning / timeout / error). Must run BEFORE HB-WALL-DROP removes the turn. */
9239
+ async summarizeWall(cls, iteration, streaming) {
9240
+ const cfg = resolveWallSummary();
9241
+ if (!cfg.enabled || !isReasoningExhaustion(cls) || !this.helperMiddleware?.summarizeWalledThinking)
9242
+ return '';
9243
+ const t0 = Date.now();
9244
+ let block = '';
9245
+ let inputChars = 0;
9246
+ let ok = false;
9247
+ try {
9248
+ const reasoning = extractWalledReasoning(this.messageHistory);
9249
+ inputChars = reasoning.length;
9250
+ if (reasoning.trim()) {
9251
+ const reply = await withTimeout(this.helperMiddleware.summarizeWalledThinking({ reasoning: clipReasoning(reasoning, cfg.headChars, cfg.tailChars), helperModelId: this.config.reactiveMentorship?.helperModelId }), cfg.timeoutMs);
9252
+ block = formatWallSummary(typeof reply === 'string' ? reply : '');
9253
+ ok = !!block;
9254
+ }
9255
+ }
9256
+ catch {
9257
+ block = '';
9258
+ }
9259
+ const store = this.getDecisionStore();
9260
+ if (store)
9261
+ void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'wall_summary', detail: { iteration, inputChars, summaryChars: block.length, ok, ms: Date.now() - t0, streaming } }).catch(() => { });
9262
+ return block;
9263
+ }
9264
+ /** HB-OUTPUT-CAP (CORTEX_OUTPUT_CAP_TOKENS, dark): fills the action model's maxTokens when the request did not set one. */
9265
+ applyOutputCap(options) {
9266
+ const cap = resolveOutputCap();
9267
+ if (!cap)
9268
+ return;
9269
+ options.parameters = options.parameters || {};
9270
+ options.parameters.maxTokens ??= cap;
9271
+ }
9272
+ /** HB-WALL-DROP (CORTEX_WALL_DROP=on, dark): on a reasoning-exhaustion wall, drop the walled turn from the in-memory history and carry
9273
+ * the nudge on the newest tool_result; one wall_drop event per drop. Fail-open (no drop). */
9274
+ applyWallDrop(cls, nudge, iteration, streaming) {
9275
+ try {
9276
+ if (!isReasoningExhaustion(cls) || !resolveWallDrop())
9277
+ return { dropped: false };
9278
+ const r = dropWalledTurn(this.messageHistory, nudge);
9279
+ const store = this.getDecisionStore();
9280
+ if (r.dropped && store)
9281
+ void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'wall_drop', detail: { iteration, droppedChars: r.droppedChars, carrier: r.carrier, streaming } }).catch(() => { });
9282
+ return r;
9283
+ }
9284
+ catch {
9285
+ return { dropped: false };
9286
+ }
9287
+ }
9222
9288
  /**
9223
9289
  * HB-COMPUTE-NUDGE (CORTEX_COMPUTE_NUDGE=on, dark): when the response that issued this round reasoned past the threshold,
9224
9290
  * append one 'compute/test instead of deliberating' line to the tail of the newest tool_result (the HB-TURN-STATUS carrier —