@nexus-cortex/core 4.108.2 → 4.108.3

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.
@@ -38,8 +38,8 @@ import { toolFactory } from '../tools/ToolFactory.js';
38
38
  import { resolveToolProfile, resolveToolAnchor, resolveFrameProfile, resolveLiftNudge, resolveLiftPlan, resolveEndTurnResolver, resolveHeadlessDropAskUser, resolveDeferredLoading, isNarrowProfile, isToolAllowedByProfile, applyToolProfile, webToolBlocked, resolveVisionHelperModel, resolveVisionHandoffMax, resolveSliceNudge, resolveSliceBlock, resolveSliceBlockAt, resolveSliceBlockMax } from '../tools/ToolProfile.js';
39
39
  import { sliceReadFile, decideSliceBlock } from './sliceBlock.js';
40
40
  import { TurnEvidence, evaluateEndTurnGates, buildMissingEndTurnReminder } from './endTurnGates.js';
41
- import { resolveCompactionResume, extractFirstUserText, pickDropped, buildCompactionResumeReminder, isCompactionResumeMessage } from './compactionResume.js';
42
- import { renderResumeMemoryPrompt, buildRebuildInstructions, resolveCheckpointBand } from './compactionResumeTemplate.js';
41
+ import { resolveCompactionResume, pickDropped, buildCompactionResumeReminder, isCompactionResumeMessage, resolveTaskText, resumeMemoryTargetTokens, coversAll, buildRollingSeedMessage } from './compactionResume.js';
42
+ import { renderResumeMemoryPrompt, buildRebuildInstructions, resolveCheckpointBand, resolveRearmBand } from './compactionResumeTemplate.js';
43
43
  import { collectWorkspaceDelta, detectCheckCommand, runCheck, resolveJudgeGroundingConfig } from '../training/judgeEvidence.js';
44
44
  import { resolveMentorRoleConfig, describeMentorWire, describeMentorDelivery, mentorSurfaceTimeoutMs } from '../training/mentorRole.js';
45
45
  import { resolveOuterToolDeadlineMs } from './outerToolTimeout.js';
@@ -115,6 +115,7 @@ export class CortexOrchestrator {
115
115
  /** HB-COMPACTION-RESUME v2: pressure-rung checkpoint state (once per band; reused at compaction). */
116
116
  lastCheckpointBand = 0;
117
117
  lastResumeMemory = null;
118
+ pinnedTaskText = '';
118
119
  /** Pending TURN_SUMMARY_PREDICTION awaiting the user's ACTUAL next message —
119
120
  * scored + recorded at the next sendMessage (graduation-signal capture,
120
121
  * Decision Capture Layer §5). In-memory only; lost on restart by design. */
@@ -5720,8 +5721,13 @@ export class CortexOrchestrator {
5720
5721
  actualToolTokens: this.currentToolTokens,
5721
5722
  actualSystemTokens: 0
5722
5723
  };
5723
- // Calculate compaction threshold using model's configuration (with accurate overrides)
5724
- const threshold = this.contextBudgetManager.getCompactionThreshold(model, budgetOverrides);
5724
+ // Calculate compaction threshold using model's configuration (with accurate overrides).
5725
+ // CORTEX_COMPACTION_THRESHOLD_TOKENS (4.108.3, test/ops override): a positive integer replaces the card-derived threshold so the
5726
+ // checkpoint rung + compaction can be exercised in a short live run (efficacy proof before any long-horizon bench).
5727
+ const thresholdOverride = Number(String(process.env.CORTEX_COMPACTION_THRESHOLD_TOKENS ?? '').trim() || 0);
5728
+ const threshold = thresholdOverride > 0
5729
+ ? thresholdOverride
5730
+ : this.contextBudgetManager.getCompactionThreshold(model, budgetOverrides);
5725
5731
  if (this.config.debug) {
5726
5732
  console.log(`[Orchestrator Context] Current tokens: ${currentTokens}, Threshold: ${threshold} (tool reserve: ${this.currentToolTokens})`);
5727
5733
  }
@@ -5734,10 +5740,12 @@ export class CortexOrchestrator {
5734
5740
  if (band > this.lastCheckpointBand) {
5735
5741
  this.lastCheckpointBand = band;
5736
5742
  try {
5737
- const r = await this.helperMiddleware.summarizeForResume(this.convertToCanonicalMessages([...this.messageHistory]), model, 6000, renderResumeMemoryPrompt('{{CONVERSATION}}', 6000));
5743
+ const tgt = resumeMemoryTargetTokens(threshold);
5744
+ const r = await this.helperMiddleware.summarizeForResume(this.convertToCanonicalMessages([...this.messageHistory]), model, tgt, renderResumeMemoryPrompt('{{CONVERSATION}}', tgt));
5738
5745
  const text = String(r?.summary ?? '');
5739
5746
  if (text.trim()) {
5740
- this.lastResumeMemory = { text, tokensAt: currentTokens, turn: this.turnNumber, helperModelId: r?.helperModelId, cost: Number(r?.cost ?? 0) };
5747
+ this.lastResumeMemory = { text, tokensAt: currentTokens, turn: this.turnNumber, helperModelId: r?.helperModelId, cost: Number(r?.cost ?? 0),
5748
+ covered: new WeakSet(this.messageHistory) };
5741
5749
  this.writeResumeMemoryFile(text, `checkpoint band ${band} (${currentTokens}/${threshold} tokens, turn ${this.turnNumber})`);
5742
5750
  const store = this.getDecisionStore();
5743
5751
  if (store)
@@ -5762,6 +5770,8 @@ export class CortexOrchestrator {
5762
5770
  try {
5763
5771
  // Calculate available budget for history (with accurate overrides)
5764
5772
  const budget = this.contextBudgetManager.calculateBudget(model, budgetOverrides);
5773
+ if (thresholdOverride > 0)
5774
+ budget.availableForHistory = Math.floor(thresholdOverride * 0.5); // override: keep ~half the forced window
5765
5775
  // Determine selection strategy based on model configuration
5766
5776
  const strategy = model.compaction?.behavior?.compactOlder ? 'preserve-critical' : 'sliding-window';
5767
5777
  // Select messages that fit within budget (standard path — no thinking stripping)
@@ -5780,13 +5790,16 @@ export class CortexOrchestrator {
5780
5790
  // session JSONL (so a bench can count compactions), (5) .cortex/memory/resume-<session>.md is written.
5781
5791
  const compactionResume = resolveCompactionResume();
5782
5792
  const dropped = pickDropped(this.messageHistory, selectedMessages);
5783
- const taskText = extractFirstUserText(this.messageHistory);
5793
+ const taskText = resolveTaskText(this.messageHistory, this.pinnedTaskText);
5794
+ if (taskText)
5795
+ this.pinnedTaskText = taskText;
5784
5796
  let resumeText = '';
5785
5797
  let resumeHelper;
5786
5798
  let resumeCost = 0;
5787
5799
  let memorySource = 'none';
5788
- // v2: a checkpoint written within the last ~15% of the threshold is fresh enough to reuse (no second helper call)
5789
- if (compactionResume && this.lastResumeMemory && (currentTokens - this.lastResumeMemory.tokensAt) <= threshold * 0.15) {
5800
+ // v3 (4.108.3): reuse the checkpoint only if it already covered every message being dropped. The v2 token-distance test went
5801
+ // negative after the first compaction and replayed one stale checkpoint forever (the hardened live proof: 27 compactions, 0 fresh memories).
5802
+ if (compactionResume && this.lastResumeMemory && coversAll(this.lastResumeMemory.covered, dropped)) {
5790
5803
  resumeText = this.lastResumeMemory.text;
5791
5804
  resumeHelper = this.lastResumeMemory.helperModelId;
5792
5805
  resumeCost = 0;
@@ -5794,11 +5807,22 @@ export class CortexOrchestrator {
5794
5807
  }
5795
5808
  else if (compactionResume && dropped.length > 0 && this.config.useHelperModels && typeof this.helperMiddleware?.summarizeForResume === 'function') {
5796
5809
  try {
5797
- const r = await this.helperMiddleware.summarizeForResume(this.convertToCanonicalMessages(dropped), model, 6000, renderResumeMemoryPrompt('{{CONVERSATION}}', 6000));
5810
+ const tgt = resumeMemoryTargetTokens(threshold);
5811
+ // v3: roll the prior memory forward — the helper summarizes [prior memory, ...newly dropped] into ONE memory.
5812
+ const prior = this.lastResumeMemory;
5813
+ const seed = prior && prior.text.trim() ? [buildRollingSeedMessage(prior.text)] : [];
5814
+ const r = await this.helperMiddleware.summarizeForResume(this.convertToCanonicalMessages([...seed, ...dropped]), model, tgt, renderResumeMemoryPrompt('{{CONVERSATION}}', tgt));
5798
5815
  resumeText = String(r?.summary ?? '');
5799
5816
  resumeHelper = r?.helperModelId;
5800
5817
  resumeCost = Number(r?.cost ?? 0);
5801
- memorySource = resumeText.trim() ? 'dropped' : 'none';
5818
+ memorySource = resumeText.trim() ? (seed.length ? 'rolled' : 'dropped') : 'none';
5819
+ if (resumeText.trim()) {
5820
+ const covered = prior?.covered ?? new WeakSet();
5821
+ for (const m of dropped)
5822
+ if (m && typeof m === 'object')
5823
+ covered.add(m);
5824
+ this.lastResumeMemory = { text: resumeText, tokensAt: currentTokens, turn: this.turnNumber, helperModelId: resumeHelper, cost: resumeCost, covered };
5825
+ }
5802
5826
  }
5803
5827
  catch (e) {
5804
5828
  console.error('[Orchestrator Context] resume memory summary failed (degrading to task-only reminder):', e?.message ?? e);
@@ -5807,6 +5831,9 @@ export class CortexOrchestrator {
5807
5831
  // Update message history with selected messages (dropping any OLDER compaction reminder — the new one supersedes it)
5808
5832
  this.messageHistory = compactionResume ? selectedMessages.filter((m) => !isCompactionResumeMessage(m)) : selectedMessages;
5809
5833
  if (compactionResume && dropped.length > 0) {
5834
+ // v3: re-arm the checkpoint rung relative to the post-compaction level so a fresh whole-conversation memory is written
5835
+ // again as the context climbs (v2 left lastCheckpointBand at its pre-compaction high-water mark → the rung never re-fired).
5836
+ this.lastCheckpointBand = resolveRearmBand(newTokenCount, threshold);
5810
5837
  const text = buildCompactionResumeReminder({
5811
5838
  turn: this.turnNumber, droppedCount: dropped.length, keptCount: this.messageHistory.length,
5812
5839
  tokensBefore: currentTokens, tokensAfter: newTokenCount, resumeText, taskText, helperModelId: resumeHelper,