@nexus-cortex/core 4.124.37 → 4.124.39

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.
@@ -24,7 +24,7 @@ import { resolveEndTurnResolverConfig, parseResolverVerdict, effectiveMaxRejects
24
24
  import { jevAvailable, jevNoul, buildGapHoldState, GAP_HOLD_QUESTIONS } from '../training/jevGate.js'; // R173b
25
25
  import { resolveFrameConfig, FRAME_CONTRACT, FRAME_TOOL_NAME } from '../frames/frameConfig.js'; // R179
26
26
  import { FrameRunner } from '../frames/frameRunner.js'; // R179
27
- import { emptySessionUsage, addMainUsage } from '../training/usageAccounting.js'; // R190
27
+ import { emptySessionUsage, addMainUsage, addSubagentUsage } from '../training/usageAccounting.js'; // R190
28
28
  import { parseRequirementLedger, initLedger, updateLedger, ledgerCounts, ledgerHoldable, formatLedgerForWriter, formatLedgerForJudge, formatLedgerHoldMessage, buildLedgerJevState, buildLedgerJevQuestions, applyLedgerJevAnswers, looksLikeBrokenCheck, ledgerFailVetoable, buildLedgerFailJevState, buildLedgerFailJevQuestions, applyLedgerFailJevAnswers, formatLedgerFailVetoMessage } from '../training/requirementLedger.js'; // R187 / R191 / R192
29
29
  import { isValueShapedTask, parseDerivationReply, methodsDiffer, parseValueLines, extractNumbers, reconcile, buildDerivationHoldMessage, isDerivationCommandAllowed, checkRunPassed, checkRunBody } from '../training/independentDerivation.js'; // R176
30
30
  import { resolveDeadlineExitConfig, deadlineExitCallBudget, parseDeadlineExitVerdict } from '../training/deadlineExitMentor.js';
@@ -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
@@ -617,6 +618,8 @@ export class CortexOrchestrator {
617
618
  }
618
619
  catch { /* fail-open */ }
619
620
  }
621
+ /** The session ledger snapshot (exact main usage + helper estimates + sub-agent breakdown) — read by the agent-mode child to report its spend. */
622
+ getSessionUsage() { return this.snapshotSessionUsage(); }
620
623
  /** R190: the session usage with the helper middleware's estimated calls merged in, plus a decision-store row so trajectories carry it. */
621
624
  snapshotSessionUsage() {
622
625
  const h = this.helperMiddleware?.getHelperUsageEstimate?.();
@@ -1707,6 +1710,7 @@ export class CortexOrchestrator {
1707
1710
  if (!this.sessionTimeline) {
1708
1711
  throw new Error('Session not initialized. Call createSession() first.');
1709
1712
  }
1713
+ this.applyOutputCap(options); // HB-OUTPUT-CAP (dark)
1710
1714
  // Item 10: full-mass pre-assembly boundary — doctrine curation completes
1711
1715
  // BEFORE the turn-0 prompt is built (under defer this no-ops here and the
1712
1716
  // lift boundary awaits instead; one-shot flag makes both calls safe).
@@ -2572,6 +2576,10 @@ export class CortexOrchestrator {
2572
2576
  console.warn(`[Orchestrator] Empty response detected (${emptyClass.kind}, hadReasoning=${emptyClass.hadReasoning}, iteration=${toolCallIteration}). ` +
2573
2577
  `Retrying once with explicit completion prompt.`);
2574
2578
  const exhaustionLevel = this.armReasoningBackoffIfExhausted(emptyClass, options.parameters?.reasoningEffort ?? effectiveModel?.reasoning?.effort, toolCallIteration, currentAssistantMessage?.usage?.outputTokens, effectiveModel.provider);
2579
+ // HB-WALL-DROP (CORTEX_WALL_DROP, dark): drop the walled turn + carry the nudge on the newest tool_result (cache-preserving retry).
2580
+ const wallSummary = await this.summarizeWall(emptyClass, toolCallIteration, false); // HB-WALL-SUMMARY (dark); '' when off/failed
2581
+ const wallNudge = `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}${wallSummary}</system-reminder>`;
2582
+ const wallDrop = this.applyWallDrop(emptyClass, wallNudge, toolCallIteration, false);
2575
2583
  // R26 (2026-05-15, surfaced by A/B benchmark): the empty assistant
2576
2584
  // turn is already in messageHistory. The retry below rebuilds the
2577
2585
  // full history and re-sends it. Anthropic's Messages API tolerates
@@ -2580,8 +2588,8 @@ export class CortexOrchestrator {
2580
2588
  // the entire retry. R18b was validated on Claude, so this was
2581
2589
  // invisible until a grok run emitted an empty turn. Repair the
2582
2590
  // 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--) {
2591
+ // every provider's message-content contract is satisfied. (Skipped when HB-WALL-DROP removed the turn.)
2592
+ for (let i = wallDrop.dropped ? -1 : this.messageHistory.length - 1; i >= 0; i--) {
2585
2593
  const m = this.messageHistory[i];
2586
2594
  const isAssistant = m?.type === 'assistant' || m?.message?.role === 'assistant';
2587
2595
  if (!isAssistant)
@@ -2610,7 +2618,7 @@ export class CortexOrchestrator {
2610
2618
  role: 'user',
2611
2619
  content: [{
2612
2620
  type: 'text',
2613
- text: `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}</system-reminder>`,
2621
+ text: wallNudge,
2614
2622
  }],
2615
2623
  },
2616
2624
  timeline: {
@@ -2624,8 +2632,10 @@ export class CortexOrchestrator {
2624
2632
  apiPattern: effectiveModel.api.pattern,
2625
2633
  },
2626
2634
  };
2627
- this.messageHistory.push(followupUserMessage);
2628
- await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
2635
+ if (wallDrop.carrier !== 'tool_result') { // HB-WALL-DROP carried the nudge on the newest tool_result already
2636
+ this.messageHistory.push(followupUserMessage);
2637
+ await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
2638
+ }
2629
2639
  // Build a continuation request and re-call. Mirrors the
2630
2640
  // tool-result continuation path (lines ~1395-1466) but without
2631
2641
  // tool_result blocks.
@@ -4130,6 +4140,7 @@ export class CortexOrchestrator {
4130
4140
  if (!this.currentSessionId || !this.sessionTimeline) {
4131
4141
  throw new Error('Session not initialized. Call createSession() first.');
4132
4142
  }
4143
+ this.applyOutputCap(options); // HB-OUTPUT-CAP (dark)
4133
4144
  // Graduation-signal capture (Decision Capture Layer §5) — same as sendMessage:
4134
4145
  // score the previous turn's prediction against THIS user message + record.
4135
4146
  if (this.pendingTurnPrediction && this.pendingTurnPrediction.sessionId === this.currentSessionId) {
@@ -4778,8 +4789,12 @@ export class CortexOrchestrator {
4778
4789
  console.warn(`[Orchestrator Streaming] R32/R18b: Empty response (${emptyClass.kind}, iteration=${toolCallIteration}). ` +
4779
4790
  `Retrying with tools preserved.`);
4780
4791
  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--) {
4792
+ // HB-WALL-DROP (streaming parity).
4793
+ const wallSummary = await this.summarizeWall(emptyClass, toolCallIteration, true);
4794
+ const wallNudge = `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}${wallSummary}</system-reminder>`;
4795
+ const wallDrop = this.applyWallDrop(emptyClass, wallNudge, toolCallIteration, true);
4796
+ // R26 repair: ensure the empty assistant turn has content (xAI hard-400s on empty). Skipped when HB-WALL-DROP removed it.
4797
+ for (let i = wallDrop.dropped ? -1 : this.messageHistory.length - 1; i >= 0; i--) {
4783
4798
  const m = this.messageHistory[i];
4784
4799
  const isAssistant = m?.type === 'assistant' || m?.message?.role === 'assistant';
4785
4800
  if (!isAssistant)
@@ -4802,7 +4817,7 @@ export class CortexOrchestrator {
4802
4817
  role: 'user',
4803
4818
  content: [{
4804
4819
  type: 'text',
4805
- text: `<system-reminder>${emptyResponseNudgeFor(emptyClass, exhaustionLevel, effectiveModel?.limits?.outputTokens)}${nudgeForbidsTools(emptyClass.kind) ? ' Do not call any more tools.' : ''}</system-reminder>`,
4820
+ text: wallNudge,
4806
4821
  }],
4807
4822
  },
4808
4823
  timeline: {
@@ -4816,8 +4831,10 @@ export class CortexOrchestrator {
4816
4831
  apiPattern: effectiveModel.api.pattern,
4817
4832
  },
4818
4833
  };
4819
- this.messageHistory.push(followupUserMessage);
4820
- await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
4834
+ if (wallDrop.carrier !== 'tool_result') {
4835
+ this.messageHistory.push(followupUserMessage);
4836
+ await this.historyStore.appendMessage(this.currentSessionId, followupUserMessage);
4837
+ }
4821
4838
  try {
4822
4839
  await this.ensureHistoryFitsModel(effectiveModel);
4823
4840
  const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
@@ -7847,6 +7864,7 @@ export class CortexOrchestrator {
7847
7864
  envOverrides: { ...(timeoutSource === 'deadline' || timeoutSource === 'requested' ? { CORTEX_TURN_DEADLINE_MS: String(timeoutMs) } : {}), ...(envOverrides ?? {}) },
7848
7865
  toolUseId: toolUse.id,
7849
7866
  });
7867
+ this.sessionUsage = addSubagentUsage(this.sessionUsage, subAgentResult.usage); // 2026-10-01: sub-agent spend in the ledger
7850
7868
  // Clean up event listeners after completion
7851
7869
  progressListeners.cleanup();
7852
7870
  if (this.config.debug) {
@@ -8102,6 +8120,7 @@ export class CortexOrchestrator {
8102
8120
  envOverrides: { ...(timeoutSource === 'deadline' || timeoutSource === 'requested' ? { CORTEX_TURN_DEADLINE_MS: String(timeoutMs) } : {}), ...(envOverrides ?? {}) },
8103
8121
  toolUseId: toolUse.id,
8104
8122
  });
8123
+ this.sessionUsage = addSubagentUsage(this.sessionUsage, subAgentResult.usage); // 2026-10-01: sub-agent spend in the ledger
8105
8124
  progressListeners.cleanup();
8106
8125
  const formattedResult = this.formatSubAgentResultForLLM(subAgentResult);
8107
8126
  const rawContent = typeof formattedResult === 'string' ? formattedResult : JSON.stringify(formattedResult);
@@ -9219,6 +9238,57 @@ export class CortexOrchestrator {
9219
9238
  return undefined;
9220
9239
  }
9221
9240
  }
9241
+ /** HB-WALL-SUMMARY (CORTEX_WALL_SUMMARY=on, dark): one helper call condensing the walled turn's reasoning; returns the attributed block for
9242
+ * the nudge, or '' (off / not a reasoning wall / no reasoning / timeout / error). Must run BEFORE HB-WALL-DROP removes the turn. */
9243
+ async summarizeWall(cls, iteration, streaming) {
9244
+ const cfg = resolveWallSummary();
9245
+ if (!cfg.enabled || !isReasoningExhaustion(cls) || !this.helperMiddleware?.summarizeWalledThinking)
9246
+ return '';
9247
+ const t0 = Date.now();
9248
+ let block = '';
9249
+ let inputChars = 0;
9250
+ let ok = false;
9251
+ try {
9252
+ const reasoning = extractWalledReasoning(this.messageHistory);
9253
+ inputChars = reasoning.length;
9254
+ if (reasoning.trim()) {
9255
+ const reply = await withTimeout(this.helperMiddleware.summarizeWalledThinking({ reasoning: clipReasoning(reasoning, cfg.headChars, cfg.tailChars), helperModelId: this.config.reactiveMentorship?.helperModelId }), cfg.timeoutMs);
9256
+ block = formatWallSummary(typeof reply === 'string' ? reply : '');
9257
+ ok = !!block;
9258
+ }
9259
+ }
9260
+ catch {
9261
+ block = '';
9262
+ }
9263
+ const store = this.getDecisionStore();
9264
+ if (store)
9265
+ void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'wall_summary', detail: { iteration, inputChars, summaryChars: block.length, ok, ms: Date.now() - t0, streaming } }).catch(() => { });
9266
+ return block;
9267
+ }
9268
+ /** HB-OUTPUT-CAP (CORTEX_OUTPUT_CAP_TOKENS, dark): fills the action model's maxTokens when the request did not set one. */
9269
+ applyOutputCap(options) {
9270
+ const cap = resolveOutputCap();
9271
+ if (!cap)
9272
+ return;
9273
+ options.parameters = options.parameters || {};
9274
+ options.parameters.maxTokens ??= cap;
9275
+ }
9276
+ /** HB-WALL-DROP (CORTEX_WALL_DROP=on, dark): on a reasoning-exhaustion wall, drop the walled turn from the in-memory history and carry
9277
+ * the nudge on the newest tool_result; one wall_drop event per drop. Fail-open (no drop). */
9278
+ applyWallDrop(cls, nudge, iteration, streaming) {
9279
+ try {
9280
+ if (!isReasoningExhaustion(cls) || !resolveWallDrop())
9281
+ return { dropped: false };
9282
+ const r = dropWalledTurn(this.messageHistory, nudge);
9283
+ const store = this.getDecisionStore();
9284
+ if (r.dropped && store)
9285
+ void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'wall_drop', detail: { iteration, droppedChars: r.droppedChars, carrier: r.carrier, streaming } }).catch(() => { });
9286
+ return r;
9287
+ }
9288
+ catch {
9289
+ return { dropped: false };
9290
+ }
9291
+ }
9222
9292
  /**
9223
9293
  * HB-COMPUTE-NUDGE (CORTEX_COMPUTE_NUDGE=on, dark): when the response that issued this round reasoned past the threshold,
9224
9294
  * append one 'compute/test instead of deliberating' line to the tail of the newest tool_result (the HB-TURN-STATUS carrier —