@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.
- package/.env.defaults +19 -0
- package/dist/config/RuntimeConfigRegistry.d.ts.map +1 -1
- package/dist/config/RuntimeConfigRegistry.js +5 -0
- package/dist/config/RuntimeConfigRegistry.js.map +1 -1
- package/dist/config/SettingsLoader.d.ts.map +1 -1
- package/dist/config/SettingsLoader.js +5 -0
- package/dist/config/SettingsLoader.js.map +1 -1
- package/dist/config/SettingsSchema.d.ts +5 -0
- package/dist/config/SettingsSchema.d.ts.map +1 -1
- package/dist/config/SettingsSchema.js +21 -0
- package/dist/config/SettingsSchema.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +5 -0
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/frames/frameConfig.d.ts +17 -0
- package/dist/frames/frameConfig.d.ts.map +1 -0
- package/dist/frames/frameConfig.js +20 -0
- package/dist/frames/frameConfig.js.map +1 -0
- package/dist/frames/frameRunner.d.ts +46 -0
- package/dist/frames/frameRunner.d.ts.map +1 -0
- package/dist/frames/frameRunner.js +168 -0
- package/dist/frames/frameRunner.js.map +1 -0
- package/dist/frames/terminusFrame.d.ts +2 -2
- package/dist/frames/terminusFrame.d.ts.map +1 -1
- package/dist/frames/terminusFrame.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts +4 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +47 -6
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/tools/registries/BaseToolRegistry.d.ts.map +1 -1
- package/dist/tools/registries/BaseToolRegistry.js +33 -0
- package/dist/tools/registries/BaseToolRegistry.js.map +1 -1
- package/dist/training/DecisionStore.d.ts +1 -1
- package/dist/training/DecisionStore.d.ts.map +1 -1
- package/dist/training/DecisionStore.js.map +1 -1
- package/dist/training/independentDerivation.d.ts.map +1 -1
- package/dist/training/independentDerivation.js +12 -0
- package/dist/training/independentDerivation.js.map +1 -1
- 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:
|