0xray 3.2.0 → 3.3.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/.opencode/codex.codex +1 -1
- package/dist/CHANGELOG.md +36 -0
- package/dist/core/features-config.js +3 -0
- package/dist/integrations/grok/grok-cli.js +6 -0
- package/dist/mcps/index.js +1 -0
- package/dist/mcps/knowledge-skills/skill-invocation.server.js +3 -2
- package/dist/mcps/knowledge-skills/storyteller.server.js +167 -0
- package/dist/mcps/orchestrator/config/agent-capabilities.js +30 -11
- package/dist/mcps/orchestrator/config/memory-routing-bridge.js +70 -0
- package/dist/mcps/orchestrator/execution/execution-planner.js +38 -8
- package/dist/mcps/orchestrator/handlers/task-handler.js +55 -2
- package/dist/mcps/researcher-confidence.js +92 -0
- package/dist/mcps/researcher.server.js +19 -3
- package/dist/memory-routing/index.js +4 -0
- package/dist/memory-routing/null-provider.js +63 -0
- package/dist/memory-routing/provider-loader.js +91 -0
- package/dist/memory-routing/provider-registry.js +40 -0
- package/dist/memory-routing/types.js +7 -0
- package/dist/memory-routing/validate-config.js +32 -0
- package/dist/nucleus/thin-dispatch.js +29 -1
- package/package.json +1 -1
- package/scripts/node/install-bridges.cjs +364 -0
- package/scripts/node/postinstall.cjs +24 -83
- package/scripts/node/setup.cjs +2 -6
- package/scripts/node/universal-version-manager.js +2 -2
- package/src/mcps/index.ts +1 -0
- package/src/mcps/knowledge-skills/skill-invocation.server.ts +3 -2
- package/src/mcps/knowledge-skills/storyteller.server.ts +206 -0
- package/src/mcps/orchestrator/config/agent-capabilities.ts +49 -14
- package/src/mcps/orchestrator/config/memory-routing-bridge.ts +80 -0
- package/src/mcps/orchestrator/execution/execution-planner.ts +49 -10
- package/src/mcps/orchestrator/handlers/task-handler.ts +71 -2
- package/src/mcps/orchestrator/types.ts +17 -0
- package/src/mcps/researcher-confidence.ts +144 -0
- package/src/mcps/researcher.server.ts +28 -3
- package/src/opencode/codex.codex +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { frameworkLogger } from '../core/framework-logger.js';
|
|
2
|
+
import { getMemoryRoutingProviderSync } from '../memory-routing/index.js';
|
|
3
|
+
const TRAP_LANGUAGE = /TYPE:\s*ontological-trap|ontological[- ]trap/i;
|
|
4
|
+
const DEFAULT_MIN_CONFIDENCE_GATE = 0.55;
|
|
5
|
+
export function shouldQueryRepertoireConfidence(description, proposalType = '') {
|
|
6
|
+
const text = `${description} ${proposalType}`.trim();
|
|
7
|
+
return TRAP_LANGUAGE.test(text);
|
|
8
|
+
}
|
|
9
|
+
export function hasHighConfidencePrimitiveMatch(routingContext, minConfidence = DEFAULT_MIN_CONFIDENCE_GATE) {
|
|
10
|
+
const confidences = routingContext.signalConfidences ?? {};
|
|
11
|
+
return Object.values(confidences).some((confidence) => confidence >= minConfidence);
|
|
12
|
+
}
|
|
13
|
+
function buildTaskText(proposalTitle, proposalDescription, proposalType) {
|
|
14
|
+
return [proposalTitle, proposalDescription, proposalType].filter(Boolean).join('\n');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve Repertoire task confidence for researcher proposal analysis.
|
|
18
|
+
* Triggered by ontological-trap language or high-confidence primitive matches.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveResearcherMemoryContext(input) {
|
|
21
|
+
const proposalTitle = input.proposalTitle ?? '';
|
|
22
|
+
const proposalDescription = input.proposalDescription ?? '';
|
|
23
|
+
const proposalType = input.proposalType ?? '';
|
|
24
|
+
const taskText = buildTaskText(proposalTitle, proposalDescription, proposalType);
|
|
25
|
+
const provider = getMemoryRoutingProviderSync();
|
|
26
|
+
if (provider.id === 'null' || !provider.getTaskConfidence) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const trapLanguageDetected = shouldQueryRepertoireConfidence(proposalDescription, proposalType);
|
|
30
|
+
const routingContext = provider.buildRoutingContext(taskText);
|
|
31
|
+
const primitiveMatchDetected = hasHighConfidencePrimitiveMatch(routingContext);
|
|
32
|
+
if (!trapLanguageDetected && !primitiveMatchDetected) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const confidence = provider.getTaskConfidence({
|
|
36
|
+
id: 'researcher-proposal',
|
|
37
|
+
description: taskText,
|
|
38
|
+
type: proposalType || 'governance',
|
|
39
|
+
});
|
|
40
|
+
const matchedSignals = confidence.matchedSignals.length > 0
|
|
41
|
+
? confidence.matchedSignals
|
|
42
|
+
: routingContext.matchedSignals;
|
|
43
|
+
const context = {
|
|
44
|
+
providerId: provider.id,
|
|
45
|
+
confidence,
|
|
46
|
+
matchedSignals,
|
|
47
|
+
recommendedAgent: confidence.recommendedAgent,
|
|
48
|
+
triggeredBy: trapLanguageDetected ? 'trap-language' : 'high-confidence-primitives',
|
|
49
|
+
};
|
|
50
|
+
if (confidence.highConfidenceTrapPresent) {
|
|
51
|
+
frameworkLogger.log('mcps/researcher', 'repertoire-trap-detected', 'info', {
|
|
52
|
+
providerId: provider.id,
|
|
53
|
+
matchedSignals,
|
|
54
|
+
complexityBoost: confidence.complexityBoost,
|
|
55
|
+
recommendedAgent: confidence.recommendedAgent,
|
|
56
|
+
triggeredBy: context.triggeredBy,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return context;
|
|
60
|
+
}
|
|
61
|
+
export function buildMemoryRoutingEvidence(context) {
|
|
62
|
+
const { confidence, matchedSignals, recommendedAgent } = context;
|
|
63
|
+
const signalSummary = confidence.signals
|
|
64
|
+
.map((entry) => `${entry.name} (${entry.confidence.toFixed(2)})`)
|
|
65
|
+
.join(', ');
|
|
66
|
+
const lines = [
|
|
67
|
+
`Repertoire memory routing (${context.providerId}, trigger: ${context.triggeredBy})`,
|
|
68
|
+
`Matched signals: ${matchedSignals.join(', ') || 'none'}`,
|
|
69
|
+
`Signal confidences: ${signalSummary || 'none'}`,
|
|
70
|
+
`High-confidence trap present: ${confidence.highConfidenceTrapPresent}`,
|
|
71
|
+
`Complexity boost: ${confidence.complexityBoost}`,
|
|
72
|
+
];
|
|
73
|
+
if (recommendedAgent) {
|
|
74
|
+
lines.push(`Recommended agent for trap handling: ${recommendedAgent}`);
|
|
75
|
+
}
|
|
76
|
+
return lines;
|
|
77
|
+
}
|
|
78
|
+
export function formatMemoryRoutingBlock(context) {
|
|
79
|
+
const { confidence, matchedSignals, recommendedAgent } = context;
|
|
80
|
+
const lines = [
|
|
81
|
+
'MEMORY_ROUTING:',
|
|
82
|
+
` provider: ${context.providerId}`,
|
|
83
|
+
` trigger: ${context.triggeredBy}`,
|
|
84
|
+
` matchedSignals: ${matchedSignals.join(', ') || 'none'}`,
|
|
85
|
+
` highConfidenceTrapPresent: ${confidence.highConfidenceTrapPresent}`,
|
|
86
|
+
` complexityBoost: ${confidence.complexityBoost}`,
|
|
87
|
+
];
|
|
88
|
+
if (recommendedAgent) {
|
|
89
|
+
lines.push(` recommendedAgent: ${recommendedAgent}`);
|
|
90
|
+
}
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
}
|
|
@@ -11,6 +11,7 @@ import * as fs from "fs";
|
|
|
11
11
|
import * as path from "path";
|
|
12
12
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
13
13
|
import { tryLLMGovernance } from "../governance/llm-governance-provider.js";
|
|
14
|
+
import { buildMemoryRoutingEvidence, formatMemoryRoutingBlock, resolveResearcherMemoryContext, } from "./researcher-confidence.js";
|
|
14
15
|
import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
|
|
15
16
|
class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
16
17
|
constructor() {
|
|
@@ -377,13 +378,28 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
|
377
378
|
*/
|
|
378
379
|
async analyzeProposal(args) {
|
|
379
380
|
const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args || {};
|
|
380
|
-
const
|
|
381
|
+
const memoryContext = resolveResearcherMemoryContext({
|
|
382
|
+
proposalTitle,
|
|
383
|
+
proposalDescription,
|
|
384
|
+
proposalType,
|
|
385
|
+
});
|
|
386
|
+
const enrichedEvidence = [...(evidence || [])];
|
|
387
|
+
if (memoryContext?.confidence.highConfidenceTrapPresent) {
|
|
388
|
+
enrichedEvidence.push(...buildMemoryRoutingEvidence(memoryContext));
|
|
389
|
+
}
|
|
390
|
+
const vote = await tryLLMGovernance("researcher", proposalTitle || "", proposalDescription || "", enrichedEvidence, proposalType || "");
|
|
391
|
+
const memoryRoutingBlock = memoryContext?.confidence.highConfidenceTrapPresent
|
|
392
|
+
? `\n${formatMemoryRoutingBlock(memoryContext)}`
|
|
393
|
+
: "";
|
|
381
394
|
if (!vote) {
|
|
395
|
+
const abstainReason = memoryContext?.confidence.highConfidenceTrapPresent
|
|
396
|
+
? "No LLM governance provider configured. Repertoire detected a high-confidence ontological trap — route to the recommended agent before proceeding."
|
|
397
|
+
: "No LLM governance provider configured. Set XRAY_GOVERNANCE_LLM_ENABLED=true + XRAY_LLM_ENDPOINT, or install Hermes and run: hermes auth add xai-oauth --no-browser";
|
|
382
398
|
return {
|
|
383
399
|
content: [
|
|
384
400
|
{
|
|
385
401
|
type: "text",
|
|
386
|
-
text:
|
|
402
|
+
text: `DECISION: abstain\nCONFIDENCE: 0.50\nREASONING: ${abstainReason}${memoryRoutingBlock}`,
|
|
387
403
|
},
|
|
388
404
|
],
|
|
389
405
|
};
|
|
@@ -392,7 +408,7 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
|
|
|
392
408
|
content: [
|
|
393
409
|
{
|
|
394
410
|
type: "text",
|
|
395
|
-
text: `DECISION: ${vote.decision}\nCONFIDENCE: ${vote.confidence.toFixed(2)}\nREASONING: ${vote.reasoning}`,
|
|
411
|
+
text: `DECISION: ${vote.decision}\nCONFIDENCE: ${vote.confidence.toFixed(2)}\nREASONING: ${vote.reasoning}${memoryRoutingBlock}`,
|
|
396
412
|
},
|
|
397
413
|
],
|
|
398
414
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getMemoryRoutingProvider, getMemoryRoutingProviderSync, getMemoryRoutingConfig, resetMemoryRoutingProvider, initializeMemoryRouting, } from './provider-registry.js';
|
|
2
|
+
export { NullMemoryRoutingProvider, createMemoryRoutingProvider } from './null-provider.js';
|
|
3
|
+
export { loadMemoryRoutingProvider } from './provider-loader.js';
|
|
4
|
+
export { validateMemoryRoutingConfig } from './validate-config.js';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const EMPTY_CONTEXT = {
|
|
2
|
+
providerId: 'null',
|
|
3
|
+
matchedSignals: [],
|
|
4
|
+
matchedTags: [],
|
|
5
|
+
flags: {},
|
|
6
|
+
synthesisAvailable: false,
|
|
7
|
+
};
|
|
8
|
+
export class NullMemoryRoutingProvider {
|
|
9
|
+
id = 'null';
|
|
10
|
+
name = 'Null (no memory routing)';
|
|
11
|
+
isAvailable() {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
buildRoutingContext() {
|
|
15
|
+
return { ...EMPTY_CONTEXT };
|
|
16
|
+
}
|
|
17
|
+
enhanceAgentCapabilities(base) {
|
|
18
|
+
return new Map(base);
|
|
19
|
+
}
|
|
20
|
+
enrichTasks(tasks) {
|
|
21
|
+
return tasks;
|
|
22
|
+
}
|
|
23
|
+
buildInheritedContext() {
|
|
24
|
+
return { providerId: 'null', matchedSignals: [], flags: {} };
|
|
25
|
+
}
|
|
26
|
+
selectAgent(capabilities, requiredCapabilities, complexity) {
|
|
27
|
+
let bestAgent = null;
|
|
28
|
+
let bestScore = -1;
|
|
29
|
+
for (const [agent, caps] of capabilities) {
|
|
30
|
+
if (complexity > caps.complexityThreshold)
|
|
31
|
+
continue;
|
|
32
|
+
const matchCount = requiredCapabilities.filter((c) => caps.capabilities.includes(c)).length;
|
|
33
|
+
const score = matchCount * 10 + caps.concurrentTasks;
|
|
34
|
+
if (score > bestScore) {
|
|
35
|
+
bestScore = score;
|
|
36
|
+
bestAgent = agent;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return bestAgent;
|
|
40
|
+
}
|
|
41
|
+
resolveThinDispatch(baseAgent, _operation, complexityScore) {
|
|
42
|
+
return {
|
|
43
|
+
agent: baseAgent,
|
|
44
|
+
adjustedScore: complexityScore,
|
|
45
|
+
context: { ...EMPTY_CONTEXT },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
getTaskConfidence() {
|
|
49
|
+
return {
|
|
50
|
+
signals: [],
|
|
51
|
+
matchedSignals: [],
|
|
52
|
+
avgConfidence: 0,
|
|
53
|
+
maxConfidence: 0,
|
|
54
|
+
highConfidenceTrapPresent: false,
|
|
55
|
+
ontologicalTrapDetected: false,
|
|
56
|
+
complexityBoost: 0,
|
|
57
|
+
recommendedAgent: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function createMemoryRoutingProvider() {
|
|
62
|
+
return new NullMemoryRoutingProvider();
|
|
63
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { frameworkLogger } from '../core/framework-logger.js';
|
|
5
|
+
import { createMemoryRoutingProvider as createNullProvider } from './null-provider.js';
|
|
6
|
+
import { validateMemoryRoutingConfig } from './validate-config.js';
|
|
7
|
+
const REPERTOIRE_CANDIDATE_PATHS = [
|
|
8
|
+
'../repertoire/dist/provider/memory-routing-provider.js',
|
|
9
|
+
'../../repertoire/dist/provider/memory-routing-provider.js',
|
|
10
|
+
'../../../repertoire/dist/provider/memory-routing-provider.js',
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* SECURITY: features.json is a trusted configuration boundary.
|
|
14
|
+
* module_path is resolved from disk and dynamically imported without signature
|
|
15
|
+
* verification. Only grant write access to features.json to trusted operators.
|
|
16
|
+
*/
|
|
17
|
+
function resolveModulePath(configuredPath, cwd) {
|
|
18
|
+
if (!configuredPath)
|
|
19
|
+
return null;
|
|
20
|
+
const candidates = [
|
|
21
|
+
resolve(cwd, configuredPath),
|
|
22
|
+
configuredPath,
|
|
23
|
+
];
|
|
24
|
+
for (const candidate of candidates) {
|
|
25
|
+
if (existsSync(candidate))
|
|
26
|
+
return candidate;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function defaultPathForProvider(provider, cwd) {
|
|
31
|
+
if (provider !== 'repertoire')
|
|
32
|
+
return null;
|
|
33
|
+
for (const rel of REPERTOIRE_CANDIDATE_PATHS) {
|
|
34
|
+
const abs = resolve(cwd, rel);
|
|
35
|
+
if (existsSync(abs))
|
|
36
|
+
return abs;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
export async function loadMemoryRoutingProvider(config, cwd = process.cwd()) {
|
|
41
|
+
const validation = validateMemoryRoutingConfig(config);
|
|
42
|
+
if (!validation.valid) {
|
|
43
|
+
await frameworkLogger.log('memory-routing', 'config-invalid', 'error', { errors: validation.errors, provider: config.provider });
|
|
44
|
+
return createNullProvider();
|
|
45
|
+
}
|
|
46
|
+
const effective = validation.normalized;
|
|
47
|
+
if (!effective.enabled || effective.provider === 'null') {
|
|
48
|
+
return createNullProvider();
|
|
49
|
+
}
|
|
50
|
+
const modulePath = resolveModulePath(effective.module_path, cwd) ??
|
|
51
|
+
defaultPathForProvider(effective.provider, cwd);
|
|
52
|
+
if (!modulePath) {
|
|
53
|
+
await frameworkLogger.log('memory-routing', 'provider-not-found', 'error', {
|
|
54
|
+
provider: effective.provider,
|
|
55
|
+
module_path: effective.module_path,
|
|
56
|
+
message: `Provider "${effective.provider}" is configured but module_path could not be resolved. Falling back to null provider.`,
|
|
57
|
+
});
|
|
58
|
+
return createNullProvider();
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const mod = await import(pathToFileURL(modulePath).href);
|
|
62
|
+
const factory = mod.createMemoryRoutingProvider ??
|
|
63
|
+
mod.default?.createMemoryRoutingProvider;
|
|
64
|
+
if (typeof factory !== 'function') {
|
|
65
|
+
throw new Error(`Module ${modulePath} must export createMemoryRoutingProvider()`);
|
|
66
|
+
}
|
|
67
|
+
const provider = factory((effective.config ?? {}));
|
|
68
|
+
if (!provider?.id || typeof provider.isAvailable !== 'function') {
|
|
69
|
+
throw new Error(`Invalid memory routing provider from ${modulePath}`);
|
|
70
|
+
}
|
|
71
|
+
if (!provider.isAvailable()) {
|
|
72
|
+
await frameworkLogger.log('memory-routing', 'provider-unavailable', 'error', {
|
|
73
|
+
providerId: provider.id,
|
|
74
|
+
modulePath,
|
|
75
|
+
message: `Provider "${provider.id}" loaded but isAvailable() returned false. Falling back to null provider.`,
|
|
76
|
+
});
|
|
77
|
+
return createNullProvider();
|
|
78
|
+
}
|
|
79
|
+
await frameworkLogger.log('memory-routing', 'provider-loaded', 'info', { providerId: provider.id, name: provider.name, modulePath });
|
|
80
|
+
return provider;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
await frameworkLogger.log('memory-routing', 'provider-load-failed', 'error', {
|
|
84
|
+
modulePath,
|
|
85
|
+
error: err instanceof Error ? err.message : String(err),
|
|
86
|
+
cwd,
|
|
87
|
+
configDir: dirname(modulePath),
|
|
88
|
+
});
|
|
89
|
+
return createNullProvider();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { featuresConfigLoader } from '../core/features-config.js';
|
|
2
|
+
import { NullMemoryRoutingProvider } from './null-provider.js';
|
|
3
|
+
import { loadMemoryRoutingProvider } from './provider-loader.js';
|
|
4
|
+
import { validateMemoryRoutingConfig } from './validate-config.js';
|
|
5
|
+
let cachedProvider = null;
|
|
6
|
+
let loadPromise = null;
|
|
7
|
+
const nullProviderSingleton = new NullMemoryRoutingProvider();
|
|
8
|
+
export function getMemoryRoutingConfig() {
|
|
9
|
+
const raw = featuresConfigLoader.loadConfig().memory_routing;
|
|
10
|
+
const validation = validateMemoryRoutingConfig(raw ?? { enabled: false, provider: 'null' });
|
|
11
|
+
return validation.normalized;
|
|
12
|
+
}
|
|
13
|
+
export async function getMemoryRoutingProvider(forceReload = false) {
|
|
14
|
+
if (!forceReload && cachedProvider)
|
|
15
|
+
return cachedProvider;
|
|
16
|
+
if (!forceReload && loadPromise)
|
|
17
|
+
return loadPromise;
|
|
18
|
+
const config = getMemoryRoutingConfig();
|
|
19
|
+
loadPromise = loadMemoryRoutingProvider(config).then((provider) => {
|
|
20
|
+
cachedProvider = provider;
|
|
21
|
+
loadPromise = null;
|
|
22
|
+
return provider;
|
|
23
|
+
});
|
|
24
|
+
return loadPromise;
|
|
25
|
+
}
|
|
26
|
+
/** Synchronous accessor — returns shared null singleton until async load completes. */
|
|
27
|
+
export function getMemoryRoutingProviderSync() {
|
|
28
|
+
return cachedProvider ?? nullProviderSingleton;
|
|
29
|
+
}
|
|
30
|
+
export function resetMemoryRoutingProvider() {
|
|
31
|
+
cachedProvider = null;
|
|
32
|
+
loadPromise = null;
|
|
33
|
+
}
|
|
34
|
+
/** Kick off background load at module init. */
|
|
35
|
+
export function initializeMemoryRouting() {
|
|
36
|
+
const config = getMemoryRoutingConfig();
|
|
37
|
+
if (config.enabled) {
|
|
38
|
+
void getMemoryRoutingProvider();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const VALID_PROVIDERS = new Set(['null', 'repertoire', 'custom']);
|
|
2
|
+
export function validateMemoryRoutingConfig(raw) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
const input = (raw ?? {});
|
|
5
|
+
const enabled = input.enabled === true;
|
|
6
|
+
const provider = typeof input.provider === 'string' ? input.provider : 'null';
|
|
7
|
+
if (!VALID_PROVIDERS.has(provider)) {
|
|
8
|
+
errors.push(`Invalid memory_routing.provider "${provider}". Must be one of: null, repertoire, custom.`);
|
|
9
|
+
}
|
|
10
|
+
if (enabled && provider !== 'null' && !input.module_path) {
|
|
11
|
+
errors.push(`memory_routing.provider "${provider}" requires module_path when enabled.`);
|
|
12
|
+
}
|
|
13
|
+
if (input.module_path !== undefined && typeof input.module_path !== 'string') {
|
|
14
|
+
errors.push('memory_routing.module_path must be a string.');
|
|
15
|
+
}
|
|
16
|
+
if (input.config !== undefined && (typeof input.config !== 'object' || input.config === null)) {
|
|
17
|
+
errors.push('memory_routing.config must be an object.');
|
|
18
|
+
}
|
|
19
|
+
const normalized = {
|
|
20
|
+
enabled,
|
|
21
|
+
provider: VALID_PROVIDERS.has(provider)
|
|
22
|
+
? provider
|
|
23
|
+
: 'null',
|
|
24
|
+
};
|
|
25
|
+
if (typeof input.module_path === 'string') {
|
|
26
|
+
normalized.module_path = input.module_path;
|
|
27
|
+
}
|
|
28
|
+
if (input.config && typeof input.config === 'object') {
|
|
29
|
+
normalized.config = input.config;
|
|
30
|
+
}
|
|
31
|
+
return { valid: errors.length === 0, errors, normalized };
|
|
32
|
+
}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
10
10
|
import { getLevelFromScore, getAgentForTier, levelToTier, } from "../delegation/complexity-core.js";
|
|
11
11
|
import { ComplexityAnalyzer } from "../delegation/complexity-analyzer.js";
|
|
12
|
+
import { getMemoryRoutingProviderSync, initializeMemoryRouting, } from "../memory-routing/index.js";
|
|
13
|
+
initializeMemoryRouting();
|
|
12
14
|
export const NUCLEUS_THIN_DISPATCH_VERSION = "0.1.0";
|
|
13
15
|
/**
|
|
14
16
|
* Score the complexity of an operation given its description and context.
|
|
@@ -52,6 +54,32 @@ export function routeToAgent(scoreOrLevel, level) {
|
|
|
52
54
|
*/
|
|
53
55
|
export function scoreAndRoute(operation, context, thresholds) {
|
|
54
56
|
const score = scoreComplexity(operation, context, thresholds);
|
|
55
|
-
|
|
57
|
+
let agent = routeToAgent(score);
|
|
58
|
+
const provider = getMemoryRoutingProviderSync();
|
|
59
|
+
let memoryRouting;
|
|
60
|
+
if (provider.id !== "null") {
|
|
61
|
+
const resolved = provider.resolveThinDispatch(agent, operation, score.score);
|
|
62
|
+
agent = resolved.agent;
|
|
63
|
+
memoryRouting = {
|
|
64
|
+
providerId: resolved.context.providerId,
|
|
65
|
+
adjustedScore: resolved.adjustedScore,
|
|
66
|
+
signals: resolved.context.matchedSignals,
|
|
67
|
+
};
|
|
68
|
+
const adjustedLevel = getLevelFromScore(resolved.adjustedScore, thresholds);
|
|
69
|
+
frameworkLogger.log("nucleus-thin-dispatch", "memory-routing", "info", {
|
|
70
|
+
providerId: resolved.context.providerId,
|
|
71
|
+
baseAgent: routeToAgent(score),
|
|
72
|
+
resolvedAgent: agent,
|
|
73
|
+
baseScore: score.score,
|
|
74
|
+
adjustedScore: resolved.adjustedScore,
|
|
75
|
+
level: adjustedLevel,
|
|
76
|
+
signals: resolved.context.matchedSignals,
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
score: { ...score, score: resolved.adjustedScore, level: adjustedLevel },
|
|
80
|
+
agent,
|
|
81
|
+
memoryRouting,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
56
84
|
return { score, agent };
|
|
57
85
|
}
|