@nexus-cortex/core 4.123.3 → 4.124.1

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 (39) hide show
  1. package/.env.defaults +19 -0
  2. package/dist/config/RuntimeConfigRegistry.d.ts.map +1 -1
  3. package/dist/config/RuntimeConfigRegistry.js +5 -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 +5 -0
  7. package/dist/config/SettingsLoader.js.map +1 -1
  8. package/dist/config/SettingsSchema.d.ts +5 -0
  9. package/dist/config/SettingsSchema.d.ts.map +1 -1
  10. package/dist/config/SettingsSchema.js +21 -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 +5 -0
  14. package/dist/config/effectiveConfig.js.map +1 -1
  15. package/dist/frames/frameConfig.d.ts +17 -0
  16. package/dist/frames/frameConfig.d.ts.map +1 -0
  17. package/dist/frames/frameConfig.js +20 -0
  18. package/dist/frames/frameConfig.js.map +1 -0
  19. package/dist/frames/frameRunner.d.ts +46 -0
  20. package/dist/frames/frameRunner.d.ts.map +1 -0
  21. package/dist/frames/frameRunner.js +168 -0
  22. package/dist/frames/frameRunner.js.map +1 -0
  23. package/dist/frames/terminusFrame.d.ts +2 -2
  24. package/dist/frames/terminusFrame.d.ts.map +1 -1
  25. package/dist/frames/terminusFrame.js.map +1 -1
  26. package/dist/orchestrator/CortexOrchestrator.d.ts +4 -0
  27. package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
  28. package/dist/orchestrator/CortexOrchestrator.js +47 -6
  29. package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
  30. package/dist/tools/registries/BaseToolRegistry.d.ts.map +1 -1
  31. package/dist/tools/registries/BaseToolRegistry.js +33 -0
  32. package/dist/tools/registries/BaseToolRegistry.js.map +1 -1
  33. package/dist/training/DecisionStore.d.ts +1 -1
  34. package/dist/training/DecisionStore.d.ts.map +1 -1
  35. package/dist/training/DecisionStore.js.map +1 -1
  36. package/dist/training/independentDerivation.d.ts.map +1 -1
  37. package/dist/training/independentDerivation.js +12 -0
  38. package/dist/training/independentDerivation.js.map +1 -1
  39. package/package.json +3 -3
@@ -21,6 +21,8 @@ import { execSync } from 'node:child_process';
21
21
  import { ENV_RECON_COMMAND, resolveLiftPlanConfig, parsePlannerResponse } from '../training/liftPlanner.js';
22
22
  import { resolveEndTurnResolverConfig, parseResolverVerdict, effectiveMaxRejects, budgetedVetoEscalation, decideVetoAction, shouldFinishConfirm, buildFinishConfirmMessage, buildMeetsConfirmMessage, gapHoldable, parseSpecChecks, applyVetoFloor, holdProgressed, planSimilarity, specCheckEvidence } from '../training/endTurnResolver.js';
23
23
  import { jevAvailable, jevNoul, buildGapHoldState, GAP_HOLD_QUESTIONS } from '../training/jevGate.js'; // R173b
24
+ import { resolveFrameConfig, FRAME_CONTRACT, FRAME_TOOL_NAME } from '../frames/frameConfig.js'; // R179
25
+ import { FrameRunner } from '../frames/frameRunner.js'; // R179
24
26
  import { isValueShapedTask, parseDerivationReply, methodsDiffer, parseValueLines, extractNumbers, reconcile, buildDerivationHoldMessage, isDerivationCommandAllowed, checkRunPassed, checkRunBody } from '../training/independentDerivation.js'; // R176
25
27
  import { resolveDeadlineExitConfig, deadlineExitCallBudget, parseDeadlineExitVerdict } from '../training/deadlineExitMentor.js';
26
28
  import { isTaskShaped } from './requirementsVerification.js';
@@ -226,6 +228,7 @@ export class CortexOrchestrator {
226
228
  herdrReporter = undefined;
227
229
  herdrTurnLabel = 'turn:0';
228
230
  turnDeadlineMsActive = 0; // R133: the active turn's wall-clock budget (0 = no deadline) — sub-agent timeouts derive from what remains
231
+ frameRunner = null; // R179 HB-TERMINUS-FRAME: lazy; the tmux session lives across turns
229
232
  endTurnResolverRejects = 0; // endTurnResolver: GAP vetoes so far this task (bounded by maxRejects → fallback-accept)
230
233
  // R165 HB-JUDGE-SEMANTIC: per-turn judge state — progress since the last veto, the judge's own named checks and plan,
231
234
  // whether the one thinking-on escalation was spent, and whether the judge adjudicated this finish (regex nudges defer).
@@ -1060,7 +1063,7 @@ export class CortexOrchestrator {
1060
1063
  if (fallback && rec.compared.some((c) => c.matched !== null))
1061
1064
  rec = { ...rec, agreement: 'agree' };
1062
1065
  const differ = methodsDiffer(plan.methodAgent, plan.methodIndependent);
1063
- derivationInfo = { ...derivationInfo, methodAgent: plan.methodAgent, methodIndependent: plan.methodIndependent, methodsDiffer: differ, checks: allowed.length, checksPassed: passedOutputs.length, checkCmds: allowed.map((c) => c.slice(0, 300)), refused: refusedCmds.length, refusedCmds, fallback, checkOutputs: outputs.map((o) => o.slice(0, 600)), derived, agreement: rec.agreement, compared: rec.compared, latencyMs: Date.now() - d0 };
1066
+ derivationInfo = { ...derivationInfo, raw: String(raw ?? '').slice(0, 1500), methodAgent: plan.methodAgent, methodIndependent: plan.methodIndependent, methodsDiffer: differ, checks: allowed.length, checksPassed: passedOutputs.length, checkCmds: allowed.map((c) => c.slice(0, 300)), refused: refusedCmds.length, refusedCmds, fallback, checkOutputs: outputs.map((o) => o.slice(0, 600)), derived, agreement: rec.agreement, compared: rec.compared, latencyMs: Date.now() - d0 };
1064
1067
  if (rec.agreement === 'disagree' && differ)
1065
1068
  derivationHold = { message: buildDerivationHoldMessage({ methodIndependent: plan.methodIndependent, compared: rec.compared, remainingFrac: resolverRemainingFrac }) };
1066
1069
  console.warn(`[EndTurnResolver] R176 DERIVATION — ${rec.agreement} (${allowed.length} check(s), methodsDiffer=${differ}) in ${Date.now() - d0} ms`);
@@ -1570,16 +1573,18 @@ export class CortexOrchestrator {
1570
1573
  // Vision gate (item 7): ReadImage is offered ONLY to probe-verified vision
1571
1574
  // cards — a text-only model must never see or reach the image path.
1572
1575
  const visionOn = model?.vision === true;
1576
+ const frameCfg = resolveFrameConfig(process.env); // R179: the terminus frame exposes FrameAction (+ EndTurn) and nothing else
1573
1577
  const factoryTools = this.applyBaseToolAllowlist(toolFactory.getAllTools()
1578
+ .filter(t => frameCfg.frame === 'terminus' ? (t.name === FRAME_TOOL_NAME || t.name === 'EndTurn') : t.name !== FRAME_TOOL_NAME)
1574
1579
  .filter(t => endTurnGateOn || t.name !== 'EndTurn')
1575
1580
  .filter(t => visionOn || !!resolveVisionHelperModel(process.env) || t.name !== 'ReadImage'));
1576
1581
  // Tool-profile: MCP/management/context tools bypass ToolFactory, so the
1577
1582
  // bash-only arm must suppress them here or the surface leaks (lean keeps
1578
1583
  // them — they are part of the "guided" surface under test).
1579
1584
  const bashOnlyProfile = isNarrowProfile();
1580
- const mcpTools = this.mcpAutoInject && !bashOnlyProfile ? this.getMcpToolsAsCanonical() : [];
1581
- const mcpManagementTools = bashOnlyProfile ? [] : this.getMcpManagementTools();
1582
- const contextManagementTools = bashOnlyProfile ? [] : this.getContextManagementTools();
1585
+ const mcpTools = this.mcpAutoInject && !bashOnlyProfile && frameCfg.frame !== 'terminus' ? this.getMcpToolsAsCanonical() : [];
1586
+ const mcpManagementTools = (bashOnlyProfile || frameCfg.frame === 'terminus') ? [] : this.getMcpManagementTools();
1587
+ const contextManagementTools = (bashOnlyProfile || frameCfg.frame === 'terminus') ? [] : this.getContextManagementTools();
1583
1588
  // Per-model deferred-loading resolved ONCE per turn (card > env > settings), so every per-request
1584
1589
  // deferred gate below flips together — this is what makes the pro card's deferredToolLoading:false
1585
1590
  // toggle (or a global env) coherent instead of split-brain. Constructor SearchTools wiring keeps
@@ -1646,7 +1651,8 @@ export class CortexOrchestrator {
1646
1651
  };
1647
1652
  const split = await this.systemMessageMiddleware.injectWithSystemSplit(content, model, hasTools, middlewareContext);
1648
1653
  injectedContent = split.userContent;
1649
- this.currentStaticSystemPrompt = pinStaticSystemPrompt(this.staticSystemPromptByConversation, this.currentConversationId, split.systemPrompt);
1654
+ this.currentStaticSystemPrompt = pinStaticSystemPrompt(this.staticSystemPromptByConversation, this.currentConversationId, resolveFrameConfig(process.env).frame === 'terminus' ? `${split.systemPrompt}\n\n${FRAME_CONTRACT}` : split.systemPrompt // R179: turn-0 contract (the pin freezes it)
1655
+ );
1650
1656
  if (this.config.debug) {
1651
1657
  console.log('[Orchestrator] System messages injected via middleware (system split for cache stability)');
1652
1658
  }
@@ -4013,7 +4019,8 @@ export class CortexOrchestrator {
4013
4019
  };
4014
4020
  const split = await this.systemMessageMiddleware.injectWithSystemSplit(content, model, hasTools, middlewareContext);
4015
4021
  injectedContent = split.userContent;
4016
- this.currentStaticSystemPrompt = pinStaticSystemPrompt(this.staticSystemPromptByConversation, this.currentConversationId, split.systemPrompt);
4022
+ this.currentStaticSystemPrompt = pinStaticSystemPrompt(this.staticSystemPromptByConversation, this.currentConversationId, resolveFrameConfig(process.env).frame === 'terminus' ? `${split.systemPrompt}\n\n${FRAME_CONTRACT}` : split.systemPrompt // R179: turn-0 contract (the pin freezes it)
4023
+ );
4017
4024
  }
4018
4025
  else {
4019
4026
  injectedContent = content;
@@ -5807,6 +5814,23 @@ export class CortexOrchestrator {
5807
5814
  * tools are filtered elsewhere — never here — so an allowlisted sub-agent
5808
5815
  * still receives its injected MCP tools on top of its base whitelist.
5809
5816
  */
5817
+ /** R179: the frame runner — one per orchestrator; drives tmux through the executor registry (no model tool call, no permission gates). */
5818
+ getFrameRunner() {
5819
+ if (!this.frameRunner) {
5820
+ const cfg = resolveFrameConfig(process.env);
5821
+ this.frameRunner = new FrameRunner(cfg, {
5822
+ execute: (name, input, signal) => this.executorRegistry.execute(name, input, signal),
5823
+ recordEvent: (kind, detail) => { const st = this.getDecisionStore(); if (st)
5824
+ void st.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: kind, toolName: FRAME_TOOL_NAME, detail }).catch(() => { }); },
5825
+ jev: cfg.chooser === 'jev' && jevAvailable() ? async (state, questions) => { const r = await jevNoul(state, questions); return r ? r.probabilities : null; } : undefined,
5826
+ });
5827
+ }
5828
+ return this.frameRunner;
5829
+ }
5830
+ async runFrameAction(input, signal) {
5831
+ const elapsedMs = this.turnLoopStartMs > 0 ? Date.now() - this.turnLoopStartMs : 0;
5832
+ return this.getFrameRunner().step({ action: input, task: this.lastRealUserText(), elapsedMs, deadlineMs: this.turnDeadlineMsActive, cwd: this.config.workingDirectory || process.cwd(), sessionId: this.currentSessionId ?? 'session', signal, predictorModel: 'generator' });
5833
+ }
5810
5834
  applyBaseToolAllowlist(tools) {
5811
5835
  const allow = this.config.allowedBaseTools;
5812
5836
  if (!allow || allow.length === 0)
@@ -7033,6 +7057,23 @@ export class CortexOrchestrator {
7033
7057
  const otherResults = others.length > 0 ? await this.handleToolCalls(others, signal, structuredOutputState) : [];
7034
7058
  return [...rejectedResults, ...otherResults];
7035
7059
  }
7060
+ // R179 HB-TERMINUS-FRAME: FrameAction never reaches a real executor — the frame runner drives the pane and returns the observation.
7061
+ if (toolUseBlocks.some((t) => t.name === FRAME_TOOL_NAME)) {
7062
+ const frameCalls = toolUseBlocks.filter((t) => t.name === FRAME_TOOL_NAME);
7063
+ const otherCalls = toolUseBlocks.filter((t) => t.name !== FRAME_TOOL_NAME);
7064
+ const frameResults = [];
7065
+ for (const call of frameCalls) {
7066
+ try {
7067
+ const r = await this.runFrameAction(call.input, signal);
7068
+ frameResults.push({ tool_use_id: call.id, tool_name: call.name, content: r.content, is_error: r.isError, metadata: r.meta });
7069
+ }
7070
+ catch (e) {
7071
+ frameResults.push({ tool_use_id: call.id, tool_name: call.name, content: `Frame error: ${String(e?.message ?? e).slice(0, 300)}`, is_error: true, metadata: { frame: 'terminus', error: true } });
7072
+ }
7073
+ }
7074
+ const otherResults = otherCalls.length > 0 ? await this.handleToolCalls(otherCalls, signal, structuredOutputState) : [];
7075
+ return [...otherResults, ...frameResults];
7076
+ }
7036
7077
  // StructuredOutput (grok-build port): intercept BEFORE any dispatch — the
7037
7078
  // synthetic tool is request-scoped (never in a registry) and must NEVER
7038
7079
  // reach a real executor. Each call gets a synthesized tool_result: