@nexus-cortex/core 4.80.0 → 4.82.0

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 (37) hide show
  1. package/dist/adapters/GatewayTranslationLayer.d.ts +11 -0
  2. package/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -1
  3. package/dist/adapters/GatewayTranslationLayer.js.map +1 -1
  4. package/dist/interfaces/APITransport.d.ts +6 -0
  5. package/dist/interfaces/APITransport.d.ts.map +1 -1
  6. package/dist/middleware/HelperModelMiddleware.d.ts +17 -0
  7. package/dist/middleware/HelperModelMiddleware.d.ts.map +1 -1
  8. package/dist/middleware/HelperModelMiddleware.js +24 -0
  9. package/dist/middleware/HelperModelMiddleware.js.map +1 -1
  10. package/dist/orchestrator/CortexOrchestrator.d.ts +16 -0
  11. package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
  12. package/dist/orchestrator/CortexOrchestrator.js +115 -3
  13. package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
  14. package/dist/orchestrator/toolChoiceTranslation.d.ts +29 -0
  15. package/dist/orchestrator/toolChoiceTranslation.d.ts.map +1 -0
  16. package/dist/orchestrator/toolChoiceTranslation.js +63 -0
  17. package/dist/orchestrator/toolChoiceTranslation.js.map +1 -0
  18. package/dist/tools/registries/BaseToolRegistry.d.ts.map +1 -1
  19. package/dist/tools/registries/BaseToolRegistry.js +24 -0
  20. package/dist/tools/registries/BaseToolRegistry.js.map +1 -1
  21. package/dist/training/DecisionPriorInjector.d.ts +9 -0
  22. package/dist/training/DecisionPriorInjector.d.ts.map +1 -1
  23. package/dist/training/DecisionPriorInjector.js +24 -0
  24. package/dist/training/DecisionPriorInjector.js.map +1 -1
  25. package/dist/training/DecisionStore.d.ts +31 -1
  26. package/dist/training/DecisionStore.d.ts.map +1 -1
  27. package/dist/training/DecisionStore.js +57 -0
  28. package/dist/training/DecisionStore.js.map +1 -1
  29. package/dist/training/mentorConsult.d.ts +51 -0
  30. package/dist/training/mentorConsult.d.ts.map +1 -0
  31. package/dist/training/mentorConsult.js +73 -0
  32. package/dist/training/mentorConsult.js.map +1 -0
  33. package/dist/training/thrashDetector.d.ts +42 -0
  34. package/dist/training/thrashDetector.d.ts.map +1 -0
  35. package/dist/training/thrashDetector.js +45 -0
  36. package/dist/training/thrashDetector.js.map +1 -0
  37. package/package.json +3 -3
@@ -36,7 +36,8 @@ import { ExactRepeatTracker } from '../training/loopLadder.js';
36
36
  import { slashCommandRegistry } from '../commands/SlashCommandRegistry.js';
37
37
  import { prefixMcpToolName, parseMcpToolName } from '../mcp/mcpToolNamespacing.js';
38
38
  import { DecisionStore } from '../training/DecisionStore.js';
39
- import { formatPriorReminder, formatFamilyReminder } from '../training/DecisionPriorInjector.js';
39
+ import { formatPriorReminder, formatFamilyReminder, formatApproachReminder } from '../training/DecisionPriorInjector.js';
40
+ import { resolveConsultRung, resolveMentorConfig, bounceMessage, rateLimitedMessage, } from '../training/mentorConsult.js';
40
41
  import { classifyErrorFamily } from '../training/errorFamily.js';
41
42
  import { classifyToolOutcome } from '../training/toolOutcome.js';
42
43
  import { LoopLadder, formatLadderSignal } from '../training/loopLadder.js';
@@ -811,6 +812,11 @@ export class CortexOrchestrator {
811
812
  if (structuredOutputState) {
812
813
  toolsToUse = ensureStructuredOutputTool(toolsToUse, structuredOutputState);
813
814
  }
815
+ // AskForAdvice (MENTORSHIP_ASK_FOR_ADVICE_SPEC §12): append the mentor tool AFTER
816
+ // the deferred filter + anchor (standard-tier tools are otherwise stripped), so a
817
+ // thrashing model can call it without a SearchTools round-trip. Session-stable (gated
818
+ // on mentorship-active, off by default), so the tool-prefix cache is not toggled mid-run.
819
+ toolsToUse = this.ensureAskForAdviceTool(toolsToUse);
814
820
  // Reset sequential call counter at start of each user turn
815
821
  this.mentorshipMiddleware?.resetSequentialCalls(this.currentSessionId);
816
822
  const preparedRequest = this.gatewayTranslation.prepareRequest(canonicalHistory, toolsToUse, effectiveModel, {
@@ -3045,6 +3051,11 @@ export class CortexOrchestrator {
3045
3051
  if (structuredOutputState) {
3046
3052
  toolsToUse = ensureStructuredOutputTool(toolsToUse, structuredOutputState);
3047
3053
  }
3054
+ // AskForAdvice (MENTORSHIP_ASK_FOR_ADVICE_SPEC §12): append the mentor tool AFTER
3055
+ // the deferred filter + anchor (standard-tier tools are otherwise stripped), so a
3056
+ // thrashing model can call it without a SearchTools round-trip. Session-stable (gated
3057
+ // on mentorship-active, off by default), so the tool-prefix cache is not toggled mid-run.
3058
+ toolsToUse = this.ensureAskForAdviceTool(toolsToUse);
3048
3059
  // Reset sequential call counter at start of each user turn
3049
3060
  this.mentorshipMiddleware?.resetSequentialCalls(this.currentSessionId);
3050
3061
  // Input-slicing at initial (streaming) request: send only items since last checkpoint
@@ -5468,6 +5479,8 @@ export class CortexOrchestrator {
5468
5479
  'MemoryRecall'
5469
5480
  ];
5470
5481
  const isContextManagementTool = contextManagementToolNames.includes(toolUse.name);
5482
+ // AskForAdvice (MENTORSHIP_ASK_FOR_ADVICE_SPEC): orchestrator-dispatched mentor tool.
5483
+ const isMentorTool = toolUse.name === 'AskForAdvice';
5471
5484
  // Phase 2.6: Check if this is an MCP management tool
5472
5485
  const mcpManagementToolNames = [
5473
5486
  'ListAvailableMcpServers',
@@ -5482,8 +5495,8 @@ export class CortexOrchestrator {
5482
5495
  // Phase 2.5 Day 4: Check if this is an MCP tool
5483
5496
  const mcpServerName = this.getMcpServerForTool(toolUse.name);
5484
5497
  const isMcpTool = mcpServerName !== undefined;
5485
- // Check if tool exists (context management, MCP management, MCP, or executor)
5486
- if (!isContextManagementTool && !isMcpManagementTool && !isMcpTool && !this.executorRegistry.hasExecutor(toolUse.name)) {
5498
+ // Check if tool exists (context management, mentor, MCP management, MCP, or executor)
5499
+ if (!isContextManagementTool && !isMentorTool && !isMcpManagementTool && !isMcpTool && !this.executorRegistry.hasExecutor(toolUse.name)) {
5487
5500
  const availableExecutors = this.executorRegistry.getExecutorNames();
5488
5501
  const availableMcpTools = this.mcpManager ? this.mcpManager.getAllTools().map(t => t.name) : [];
5489
5502
  const allAvailable = [...availableExecutors, ...availableMcpTools, ...contextManagementToolNames, ...mcpManagementToolNames];
@@ -5567,6 +5580,10 @@ export class CortexOrchestrator {
5567
5580
  }
5568
5581
  };
5569
5582
  }
5583
+ // AskForAdvice — consult the stronger mentor for a hint (spec §4-§5)
5584
+ else if (isMentorTool) {
5585
+ result = await this.executeAskForAdvice(toolUse.input);
5586
+ }
5570
5587
  // Phase 2.6: Execute MCP management tool
5571
5588
  else if (isMcpManagementTool) {
5572
5589
  if (this.config.debug) {
@@ -6699,6 +6716,82 @@ export class CortexOrchestrator {
6699
6716
  * Returns the (possibly-augmented) result. Failures in the store path
6700
6717
  * are swallowed so training never breaks tool execution.
6701
6718
  */
6719
+ /** Include AskForAdvice in the turn's tool set when mentorship is active (spec §12).
6720
+ * Appended post-filter so the deferred filter can't strip this standard-tier tool;
6721
+ * gated on reactiveMentorship.enabled (off by default) and idempotent. */
6722
+ ensureAskForAdviceTool(tools) {
6723
+ if (!this.config.reactiveMentorship?.enabled || !tools)
6724
+ return tools;
6725
+ if (tools.some((t) => t.name === 'AskForAdvice'))
6726
+ return tools;
6727
+ const def = toolFactory.getTool('AskForAdvice');
6728
+ return def ? [...tools, def] : tools;
6729
+ }
6730
+ /** Honored AskForAdvice consults per session (rate-limit + rung state; persists across turns). */
6731
+ mentorConsultCounts = new Map();
6732
+ /**
6733
+ * AskForAdvice executor (MENTORSHIP_ASK_FOR_ADVICE_SPEC v1). Orchestrator-dispatched
6734
+ * because it needs helperMiddleware + the decision store + session state, which the
6735
+ * packages/executors executors structurally lack. Graduated ladder: premature bounce →
6736
+ * mentor DIRECTED REFRAME → structured interview → rate-limited. The stronger mentor
6737
+ * (reactiveMentorship.helperModelId, e.g. deepseek-v4-pro) returns a HINT, never the
6738
+ * solution. Banks a `mentor_consult` episode for the apprentice data lake. Fail-open —
6739
+ * a mentor error never breaks the turn.
6740
+ */
6741
+ async executeAskForAdvice(input) {
6742
+ const sessionId = this.currentSessionId ?? 'unknown';
6743
+ const store = this.getDecisionStore();
6744
+ const failedRows = store ? await store.recentFailures(6) : [];
6745
+ const failed = failedRows.map((d) => ({
6746
+ call: d.inputSummary || d.toolName,
6747
+ error: d.errorSnippet || '',
6748
+ }));
6749
+ const honored = this.mentorConsultCounts.get(sessionId) ?? 0;
6750
+ // v1 gate: the model self-selected by CALLING the tool, so a light "real struggle
6751
+ // exists" check (>=2 recent failures) suffices. The full thrashDetector drives the
6752
+ // proactive invite/forced-choice path (v2), not this executor.
6753
+ const thrashing = failed.length >= 2;
6754
+ const rung = resolveConsultRung(honored, thrashing);
6755
+ if (rung === 'bounce') {
6756
+ return { success: true, llmContent: bounceMessage(failed.length), metadata: { source: 'mentor-consult', rung } };
6757
+ }
6758
+ if (rung === 'ratelimited') {
6759
+ return { success: true, llmContent: rateLimitedMessage(resolveMentorConfig().maxConsults), metadata: { source: 'mentor-consult', rung } };
6760
+ }
6761
+ let hint;
6762
+ try {
6763
+ hint = await this.helperMiddleware.generateMentorHint({
6764
+ rung,
6765
+ task: this.lastRealUserText(),
6766
+ failed,
6767
+ question: input?.question,
6768
+ helperModelId: this.config.reactiveMentorship?.helperModelId,
6769
+ });
6770
+ }
6771
+ catch (err) {
6772
+ return {
6773
+ success: true,
6774
+ llmContent: 'Advice is unavailable right now — keep working the problem: re-read the task and try a distinct approach.',
6775
+ metadata: { source: 'mentor-consult', rung, error: String(err).slice(0, 120) },
6776
+ };
6777
+ }
6778
+ this.mentorConsultCounts.set(sessionId, honored + 1);
6779
+ if (store) {
6780
+ // Reward-labeled episode for the apprentice pipeline (data pump). Best-effort.
6781
+ store.recordEvent({
6782
+ sessionId,
6783
+ kind: 'mentor_consult',
6784
+ toolName: 'AskForAdvice',
6785
+ detail: {
6786
+ rung,
6787
+ helperModel: this.config.reactiveMentorship?.helperModelId ?? 'default',
6788
+ hint: hint.slice(0, 400),
6789
+ failedCount: failed.length,
6790
+ },
6791
+ }).catch(() => { });
6792
+ }
6793
+ return { success: true, llmContent: hint, metadata: { source: 'mentor-consult', rung, helperModel: this.config.reactiveMentorship?.helperModelId } };
6794
+ }
6702
6795
  async processToolTraining(toolUse, result) {
6703
6796
  const store = this.getDecisionStore();
6704
6797
  if (!store)
@@ -6732,11 +6825,26 @@ export class CortexOrchestrator {
6732
6825
  // failures spanning >=2 distinct inputs. Gated on the UNIFIED
6733
6826
  // outcome (not is_error) so failing-command loops are covered.
6734
6827
  const family = outcome.family ?? classifyErrorFamily(String(result.content).slice(0, 200));
6828
+ let fired = false;
6735
6829
  if (family) {
6736
6830
  const ff = await store.familyFailures(toolUse.name, family);
6737
6831
  const famReminder = formatFamilyReminder(toolUse.name, family, ff.count, ff.distinctInputs, ff.recent);
6738
6832
  if (famReminder) {
6739
6833
  augmented = { ...result, content: famReminder + result.content };
6834
+ fired = true;
6835
+ }
6836
+ }
6837
+ // Approach lens (BUILD 1a — the ×98 varied-retry class): neither the
6838
+ // exact-input nor the error-family reminder fired, but the CURRENT
6839
+ // failure may be the latest of a repeated command-SHAPE loop (same
6840
+ // approach, tweaked args, differing errors). Fires on >=2 failures of
6841
+ // this approachHash across >=2 distinct inputs — the fuzzy net the
6842
+ // exact/family lenses miss. Last, so more-specific lenses win.
6843
+ if (!fired && outcome.approachHash) {
6844
+ const af = await store.approachFailures(toolUse.name, outcome.approachHash);
6845
+ const apReminder = formatApproachReminder(toolUse.name, af.count, af.distinctInputs, af.recent);
6846
+ if (apReminder) {
6847
+ augmented = { ...result, content: apReminder + result.content };
6740
6848
  }
6741
6849
  }
6742
6850
  }
@@ -6761,6 +6869,10 @@ export class CortexOrchestrator {
6761
6869
  ...(outcome.status !== 'ok'
6762
6870
  ? { errorSnippet: String(result.content).slice(0, 200) }
6763
6871
  : {}),
6872
+ // BUILD 1a: persist the command-shape fingerprint so the cross-turn
6873
+ // approach lens (approachFailures) can see varied retries of one
6874
+ // approach. Cheap, always recorded (both lenses read failures only).
6875
+ approachHash: outcome.approachHash,
6764
6876
  });
6765
6877
  }
6766
6878
  catch (err) {