@nexus-cortex/core 4.108.9 → 4.108.11

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 (30) hide show
  1. package/.env.defaults +12 -0
  2. package/dist/config/RuntimeConfigRegistry.d.ts.map +1 -1
  3. package/dist/config/RuntimeConfigRegistry.js +1 -0
  4. package/dist/config/RuntimeConfigRegistry.js.map +1 -1
  5. package/dist/config/SettingsLoader.d.ts.map +1 -1
  6. package/dist/config/SettingsLoader.js +1 -0
  7. package/dist/config/SettingsLoader.js.map +1 -1
  8. package/dist/config/SettingsSchema.d.ts +1 -0
  9. package/dist/config/SettingsSchema.d.ts.map +1 -1
  10. package/dist/config/SettingsSchema.js +9 -0
  11. package/dist/config/SettingsSchema.js.map +1 -1
  12. package/dist/config/effectiveConfig.d.ts.map +1 -1
  13. package/dist/config/effectiveConfig.js +1 -0
  14. package/dist/config/effectiveConfig.js.map +1 -1
  15. package/dist/orchestrator/CortexOrchestrator.d.ts +3 -0
  16. package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
  17. package/dist/orchestrator/CortexOrchestrator.js +43 -14
  18. package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
  19. package/dist/orchestrator/outerToolTimeout.d.ts +11 -1
  20. package/dist/orchestrator/outerToolTimeout.d.ts.map +1 -1
  21. package/dist/orchestrator/outerToolTimeout.js +16 -2
  22. package/dist/orchestrator/outerToolTimeout.js.map +1 -1
  23. package/dist/training/DecisionStore.d.ts +1 -1
  24. package/dist/training/DecisionStore.d.ts.map +1 -1
  25. package/dist/training/DecisionStore.js.map +1 -1
  26. package/dist/training/loopLadder.d.ts +37 -1
  27. package/dist/training/loopLadder.d.ts.map +1 -1
  28. package/dist/training/loopLadder.js +73 -2
  29. package/dist/training/loopLadder.js.map +1 -1
  30. package/package.json +3 -3
@@ -43,11 +43,11 @@ import { renderResumeMemoryPrompt, buildRebuildInstructions, resolveCheckpointBa
43
43
  import { resolveCortexStateDir, cortexStatePath } from '../utils/stateDir.js';
44
44
  import { collectWorkspaceDelta, detectCheckCommand, runCheck, resolveJudgeGroundingConfig } from '../training/judgeEvidence.js';
45
45
  import { resolveMentorRoleConfig, describeMentorWire, describeMentorDelivery, mentorSurfaceTimeoutMs } from '../training/mentorRole.js';
46
- import { resolveOuterToolDeadlineMs } from './outerToolTimeout.js';
46
+ import { resolveOuterToolDeadlineMs, resolveOuterToolTimeoutFloorMs } from './outerToolTimeout.js';
47
47
  import { resolveSubAgentTimeoutMs } from './subAgentTimeout.js';
48
48
  import { resolveTurnDeadlineMs, timeBudgetState, timeBudgetWarnNudge } from './timeBudget.js';
49
49
  import { readStagedDoctrine, applyCuratedDoctrine, runOrientForStaging, withTimeout } from './doctrineCuration.js';
50
- import { ExactRepeatTracker } from '../training/loopLadder.js';
50
+ import { ExactRepeatTracker, notePollResults } from '../training/loopLadder.js';
51
51
  import { slashCommandRegistry } from '../commands/SlashCommandRegistry.js';
52
52
  import { prefixMcpToolName, parseMcpToolName } from '../mcp/mcpToolNamespacing.js';
53
53
  import { DecisionStore } from '../training/DecisionStore.js';
@@ -1582,6 +1582,10 @@ export class CortexOrchestrator {
1582
1582
  let currentAssistantCanonicalMessage = assistantCanonicalMessage;
1583
1583
  this.recordDsmlRecovery(currentAssistantCanonicalMessage);
1584
1584
  let toolCallIteration = 0;
1585
+ // R137 HB-POLL-REPEAT-BREAKER: the exact-repeat breaker used to force-exit by overwriting
1586
+ // toolCallIteration = MAX_TOOL_ITERATIONS, which banked toolCallIterations=1000 (a metrics
1587
+ // artifact). An explicit flag ends the loop the same way and keeps the true count.
1588
+ let loopBreak = false;
1585
1589
  // Loop control settings — single source of truth via getLoopControlConfig()
1586
1590
  const loopDefaults = this.getLoopControlConfig();
1587
1591
  // CORTEX_ACTION_EFFORT: static reasoning effort for the PRIMARY (action) model — fills the
@@ -1596,6 +1600,7 @@ export class CortexOrchestrator {
1596
1600
  }
1597
1601
  const MAX_TOOL_ITERATIONS = loopDefaults.maxToolIterations;
1598
1602
  const TOOL_TIMEOUT_MS = loopDefaults.toolTimeoutMs;
1603
+ const OUTER_TOOL_TIMEOUT_FLOOR_MS = loopDefaults.outerToolTimeoutFloorMs;
1599
1604
  const MAX_CONSECUTIVE_ERRORS = loopDefaults.maxConsecutiveErrors;
1600
1605
  const MAX_LOOP_REPETITIONS = loopDefaults.maxLoopRepetitions;
1601
1606
  const TOOL_BUDGET_SOFT = loopDefaults.toolBudgetSoft; // R29b brake
@@ -2312,8 +2317,9 @@ export class CortexOrchestrator {
2312
2317
  if (matchCount >= MAX_LOOP_REPETITIONS) {
2313
2318
  console.warn(`[Orchestrator Phase 2.5] Loop detected: Tool "${toolUse.name}" called ${matchCount} times with same input. Stopping to prevent infinite loop.`);
2314
2319
  console.warn(`[Orchestrator Phase 2.5] Repeated input: ${inputHash.substring(0, 100)}...`);
2315
- // Break out of the main while loop
2316
- toolCallIteration = MAX_TOOL_ITERATIONS;
2320
+ // Break out of the main while loop (R137: flag, not a counter overwrite)
2321
+ loopBreak = true;
2322
+ this.recordLoopBreak(toolUse.name, matchCount, toolCallIteration);
2317
2323
  break;
2318
2324
  }
2319
2325
  }
@@ -2321,7 +2327,7 @@ export class CortexOrchestrator {
2321
2327
  // Without this, `break` above only exits the inner for, and the full tool
2322
2328
  // batch still executes this iteration (defeats loop prevention).
2323
2329
  // Orphaned tool_use blocks are handled by orphan-recovery at loop exit.
2324
- if (toolCallIteration >= MAX_TOOL_ITERATIONS) {
2330
+ if (loopBreak || toolCallIteration >= MAX_TOOL_ITERATIONS) {
2325
2331
  break;
2326
2332
  }
2327
2333
  // Phase 2.5 Day 3: Execute tools with timeout support
@@ -2333,7 +2339,7 @@ export class CortexOrchestrator {
2333
2339
  const abortController = new AbortController();
2334
2340
  // D-A (2026-09-04): outer cap must sit ABOVE the model's REQUESTED Bash timeout, not a static
2335
2341
  // ~150s, or a legit `Bash({ timeout: 250000 })` is killed before ShellTool's own promote-at-deadline.
2336
- const outerDeadlineMs = resolveOuterToolDeadlineMs(toolUseBlocks, TOOL_TIMEOUT_MS, OUTER_TIMEOUT_GRACE_MS, this.subAgentBlockTimeoutMs);
2342
+ const outerDeadlineMs = resolveOuterToolDeadlineMs(toolUseBlocks, TOOL_TIMEOUT_MS, OUTER_TIMEOUT_GRACE_MS, this.subAgentBlockTimeoutMs, OUTER_TOOL_TIMEOUT_FLOOR_MS);
2337
2343
  const timeoutId = setTimeout(() => {
2338
2344
  console.warn(`[Orchestrator Phase 2.5] Tool execution timeout after ${outerDeadlineMs}ms`);
2339
2345
  abortController.abort();
@@ -2344,6 +2350,7 @@ export class CortexOrchestrator {
2344
2350
  const toolResults = await this.handleToolCalls(toolUseBlocks, abortController.signal, structuredOutputState);
2345
2351
  this.testPostExecThrow(); // test-only fault injection for the re-execution guard (inert in prod)
2346
2352
  clearTimeout(timeoutId);
2353
+ notePollResults(exactRepeatTracker, toolUseBlocks, toolResults); // R137: poll repeats count only on identical STALLED output
2347
2354
  // Unified Outcome Ladder: observe each result's TRUE outcome (exit
2348
2355
  // codes, not wire is_error) and escalate on repeated failing
2349
2356
  // approaches. The strongest signal this batch is injected at the
@@ -2978,8 +2985,11 @@ export class CortexOrchestrator {
2978
2985
  break;
2979
2986
  }
2980
2987
  }
2981
- // Warn if we hit max iterations
2982
- if (toolCallIteration >= MAX_TOOL_ITERATIONS) {
2988
+ // Warn if we hit max iterations (or the exact-repeat breaker ended the loop — R137)
2989
+ if (loopBreak) {
2990
+ console.warn(`[Orchestrator Phase 2.5] Exact-repeat breaker ended the tool loop at iteration ${toolCallIteration}. Stopping loop.`);
2991
+ }
2992
+ else if (toolCallIteration >= MAX_TOOL_ITERATIONS) {
2983
2993
  console.warn(`[Orchestrator Phase 2.5] Max tool iterations (${MAX_TOOL_ITERATIONS}) reached. Stopping loop.`);
2984
2994
  }
2985
2995
  // R29a: post-loop synthesis net. The tool loop can exit with the final
@@ -3743,6 +3753,7 @@ export class CortexOrchestrator {
3743
3753
  // Phase 2.8: Multi-turn tool execution loop (STREAMING VERSION)
3744
3754
  // Matches sendMessage() pattern exactly (lines 800-1229), but with streaming
3745
3755
  let toolCallIteration = 0;
3756
+ let loopBreak = false; // R137: explicit exact-repeat break flag (parity with sendMessage; keeps the true iteration count)
3746
3757
  let totalToolErrors = 0;
3747
3758
  let emptyResponseRetryUsed = false; // R32: parity with sendMessage R18b guard
3748
3759
  let inactionNudgeUsed = false; // Inaction guard (backlog item 2) — streaming parity
@@ -3792,6 +3803,7 @@ export class CortexOrchestrator {
3792
3803
  const MAX_CONSECUTIVE_ERRORS = loopDefaults.maxConsecutiveErrors;
3793
3804
  const MAX_LOOP_REPETITIONS = loopDefaults.maxLoopRepetitions;
3794
3805
  const TOOL_TIMEOUT_MS = loopDefaults.toolTimeoutMs;
3806
+ const OUTER_TOOL_TIMEOUT_FLOOR_MS = loopDefaults.outerToolTimeoutFloorMs;
3795
3807
  const TOOL_BUDGET_SOFT = loopDefaults.toolBudgetSoft; // R29b brake
3796
3808
  // R64: TOOL_BUDGET_SOFT <= 0 disables the budget-pressure system entirely
3797
3809
  // (no reminders, no force-synthesis cap). Runaway protection then rests on
@@ -4125,7 +4137,8 @@ export class CortexOrchestrator {
4125
4137
  if (this.config.debug) {
4126
4138
  console.warn(`[Orchestrator Streaming] Loop detected: Tool "${toolUse.name}" called ${matchCount} times with same input. Stopping.`);
4127
4139
  }
4128
- toolCallIteration = MAX_TOOL_ITERATIONS; // Force exit
4140
+ loopBreak = true; // Force exit (R137: flag, not a counter overwrite)
4141
+ this.recordLoopBreak(toolUse.name, matchCount, toolCallIteration);
4129
4142
  break;
4130
4143
  }
4131
4144
  }
@@ -4133,14 +4146,14 @@ export class CortexOrchestrator {
4133
4146
  // Without this, `break` above only exits the inner for, and the full tool
4134
4147
  // batch still executes this iteration (defeats loop prevention).
4135
4148
  // Orphaned tool_use blocks are handled by orphan-recovery at loop exit.
4136
- if (toolCallIteration >= MAX_TOOL_ITERATIONS) {
4149
+ if (loopBreak || toolCallIteration >= MAX_TOOL_ITERATIONS) {
4137
4150
  break;
4138
4151
  }
4139
4152
  // Execute tools with timeout (outer cap fires a grace period AFTER the tool's own deadline —
4140
4153
  // see the non-stream site: ShellTool's promote-at-deadline must win the race; 4.90.1)
4141
4154
  const abortController = new AbortController();
4142
4155
  // D-A (2026-09-04): outer cap sits above the requested Bash timeout (see the non-stream site).
4143
- const outerDeadlineMs = resolveOuterToolDeadlineMs(toolUseBlocks, TOOL_TIMEOUT_MS, OUTER_TIMEOUT_GRACE_MS, this.subAgentBlockTimeoutMs);
4156
+ const outerDeadlineMs = resolveOuterToolDeadlineMs(toolUseBlocks, TOOL_TIMEOUT_MS, OUTER_TIMEOUT_GRACE_MS, this.subAgentBlockTimeoutMs, OUTER_TOOL_TIMEOUT_FLOOR_MS);
4144
4157
  const timeoutId = setTimeout(() => {
4145
4158
  if (this.config.debug) {
4146
4159
  console.warn(`[Orchestrator Streaming] Tool execution timeout after ${outerDeadlineMs}ms`);
@@ -4152,6 +4165,7 @@ export class CortexOrchestrator {
4152
4165
  const toolResults = await this.handleToolCalls(toolUseBlocks, abortController.signal, structuredOutputState);
4153
4166
  this.testPostExecThrow(); // test-only fault injection for the re-execution guard (inert in prod)
4154
4167
  clearTimeout(timeoutId);
4168
+ notePollResults(streamRepeatTracker, toolUseBlocks, toolResults); // R137 (streaming parity)
4155
4169
  // 4.91.0: EndTurn gates for STREAMING (were sendMessage-only) — same evidence + evaluation.
4156
4170
  ev.noteToolResults(toolResults);
4157
4171
  evaluateEndTurnGates(ev, toolResults, toolUseBlocks, this.gateDeps());
@@ -4791,10 +4805,12 @@ export class CortexOrchestrator {
4791
4805
  }
4792
4806
  }
4793
4807
  }
4794
- // Warn if hit max iterations
4795
- if (toolCallIteration >= MAX_TOOL_ITERATIONS) {
4808
+ // Warn if hit max iterations (or the exact-repeat breaker ended the loop — R137)
4809
+ if (loopBreak || toolCallIteration >= MAX_TOOL_ITERATIONS) {
4796
4810
  if (this.config.debug) {
4797
- console.warn(`[Orchestrator Streaming] Max tool iterations (${MAX_TOOL_ITERATIONS}) reached. Stopping loop.`);
4811
+ console.warn(loopBreak
4812
+ ? `[Orchestrator Streaming] Exact-repeat breaker ended the tool loop at iteration ${toolCallIteration}. Stopping loop.`
4813
+ : `[Orchestrator Streaming] Max tool iterations (${MAX_TOOL_ITERATIONS}) reached. Stopping loop.`);
4798
4814
  }
4799
4815
  }
4800
4816
  // R29a (streaming parity): post-loop synthesis net. R32 added the R18b
@@ -5138,6 +5154,8 @@ export class CortexOrchestrator {
5138
5154
  // coercion silently replaced an explicit 0 with 15.
5139
5155
  toolBudgetSoft: Number.isFinite(softRaw) && softRaw >= 0 ? softRaw : 400,
5140
5156
  toolTimeoutMs: this.config.loopControl?.toolTimeoutMs ?? 120000,
5157
+ // CORTEX_OUTER_TOOL_TIMEOUT_MS: floor for the outer per-batch abort (0 = no floor, byte-identical).
5158
+ outerToolTimeoutFloorMs: resolveOuterToolTimeoutFloorMs(),
5141
5159
  maxLoopRepetitions: this.config.loopControl?.maxLoopRepetitions ?? 5,
5142
5160
  // #2 wall-clock deadline: opt-in (0 = disabled), env CORTEX_TURN_DEADLINE_MS is the fallback.
5143
5161
  turnDeadlineMs: resolveTurnDeadlineMs(this.config.loopControl?.turnDeadlineMs),
@@ -6262,6 +6280,17 @@ export class CortexOrchestrator {
6262
6280
  * @param signal AbortSignal for cancellation
6263
6281
  * @returns Array of tool results
6264
6282
  */
6283
+ /** R137 HB-POLL-REPEAT-BREAKER: bank the exact-repeat break as a decisions event (mechanism-engagement evidence). */
6284
+ recordLoopBreak(tool, matchCount, iteration) {
6285
+ const store = this.getDecisionStore();
6286
+ if (store)
6287
+ void store.recordEvent({
6288
+ sessionId: this.currentSessionId ?? 'unknown',
6289
+ kind: 'loop_break',
6290
+ toolName: tool,
6291
+ detail: { tool, matchCount, iteration },
6292
+ }).catch(() => { });
6293
+ }
6265
6294
  async handleToolCalls(toolUseBlocks, signal, structuredOutputState) {
6266
6295
  // StructuredOutput (grok-build port): intercept BEFORE any dispatch — the
6267
6296
  // synthetic tool is request-scoped (never in a registry) and must NEVER