@aikdna/kdna-core 0.1.0 → 0.2.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/package.json +1 -1
- package/src/compose.js +123 -0
- package/src/index.js +2 -0
- package/src/index.mjs +2 -0
- package/src/lint-pure.js +19 -0
- package/src/types.d.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikdna/kdna-core",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "KDNA core library — pure logic for loading, validating, linting, and rendering KDNA domain cognition packages. Zero Node.js dependencies.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "src/index.js",
|
package/src/compose.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KDNA Compose — Multi-domain composition logic.
|
|
3
|
+
*
|
|
4
|
+
* Merges judgment constraints from multiple domains into a single
|
|
5
|
+
* agent context. Domains contribute independently; conflicts are
|
|
6
|
+
* surfaced, not silently resolved.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { formatContext } = require('./loader');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Compose multiple loaded domains into a single agent context string.
|
|
13
|
+
*
|
|
14
|
+
* Each domain contributes its own section. If two domains define
|
|
15
|
+
* conflicting axioms or banned terms, both are included — the agent
|
|
16
|
+
* must report the conflict, not pick one.
|
|
17
|
+
*
|
|
18
|
+
* @param {Array<{core:object, patterns:object}>} domains
|
|
19
|
+
* @param {object} [options]
|
|
20
|
+
* @param {string} [options.separator] — section separator
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function composeContext(domains, options = {}) {
|
|
24
|
+
if (!domains || !domains.length) return '';
|
|
25
|
+
|
|
26
|
+
const separator = options.separator || '\n\n---\n\n';
|
|
27
|
+
|
|
28
|
+
return domains
|
|
29
|
+
.filter((d) => d && d.core && d.patterns)
|
|
30
|
+
.map((d) => formatContext(d))
|
|
31
|
+
.filter((ctx) => ctx)
|
|
32
|
+
.join(separator);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Match user input against domain trigger_signals to determine
|
|
37
|
+
* which domains should be activated.
|
|
38
|
+
*
|
|
39
|
+
* Each domain can define trigger_signals in its core (array of
|
|
40
|
+
* keywords or phrases). This function checks if any signal matches
|
|
41
|
+
* the input and returns the list of matching domain indices.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} input — user task description
|
|
44
|
+
* @param {Array<{id:string, core:{trigger_signals?:string[]}}>} domains
|
|
45
|
+
* @returns {number[]} — indices of matching domains
|
|
46
|
+
*/
|
|
47
|
+
function classifySignals(input, domains) {
|
|
48
|
+
if (!input || !domains || !domains.length) return [];
|
|
49
|
+
|
|
50
|
+
const lower = input.toLowerCase();
|
|
51
|
+
const active = [];
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < domains.length; i++) {
|
|
54
|
+
const domain = domains[i];
|
|
55
|
+
const signals = domain.core?.trigger_signals || [];
|
|
56
|
+
|
|
57
|
+
if (!signals.length) {
|
|
58
|
+
// No signals defined → domain is primary (always active)
|
|
59
|
+
active.push(i);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const matched = signals.some((signal) => lower.includes(signal.toLowerCase()));
|
|
64
|
+
if (matched) active.push(i);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return active;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Compose self-check items from multiple domains into a single
|
|
72
|
+
* checklist. Each domain's checks are prefixed with its domain name
|
|
73
|
+
* so conflicts are visible.
|
|
74
|
+
*
|
|
75
|
+
* @param {Array<{id:string, core:{meta:{domain:string}}, patterns:{self_check:string[]}}>} domains
|
|
76
|
+
* @returns {string[]}
|
|
77
|
+
*/
|
|
78
|
+
function composeChecks(domains) {
|
|
79
|
+
if (!domains || !domains.length) return [];
|
|
80
|
+
|
|
81
|
+
const checks = [];
|
|
82
|
+
|
|
83
|
+
for (const domain of domains) {
|
|
84
|
+
const name = domain.core?.meta?.domain || domain.id || 'unknown';
|
|
85
|
+
const items = domain.patterns?.self_check || [];
|
|
86
|
+
|
|
87
|
+
if (!items.length) continue;
|
|
88
|
+
|
|
89
|
+
if (domains.length === 1) {
|
|
90
|
+
checks.push(...items);
|
|
91
|
+
} else {
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
checks.push(`[${name}] ${item}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return checks;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Load multiple domains from data maps and compose their context.
|
|
103
|
+
* Convenience function: loads each domain, then composes.
|
|
104
|
+
*
|
|
105
|
+
* @param {Array<object>} dataMaps — array of file data maps
|
|
106
|
+
* @param {object} [options] — passed to loadDomainFromFiles + composeContext
|
|
107
|
+
* @returns {{ domains: Array, context: string, activeIndices: number[] }}
|
|
108
|
+
*/
|
|
109
|
+
function loadAndCompose(dataMaps, options = {}) {
|
|
110
|
+
const { loadDomainFromFiles } = require('./loader');
|
|
111
|
+
|
|
112
|
+
const domains = dataMaps.map((dm) => loadDomainFromFiles(dm, options)).filter(Boolean);
|
|
113
|
+
|
|
114
|
+
const { input = '' } = options;
|
|
115
|
+
const activeIndices = classifySignals(input, domains);
|
|
116
|
+
|
|
117
|
+
const activeDomains = activeIndices.map((i) => domains[i]);
|
|
118
|
+
const context = composeContext(activeDomains, options);
|
|
119
|
+
|
|
120
|
+
return { domains, context, activeIndices };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { composeContext, classifySignals, composeChecks, loadAndCompose };
|
package/src/index.js
CHANGED
|
@@ -5,10 +5,12 @@ const loader = require('./loader');
|
|
|
5
5
|
const lint = require('./lint-pure');
|
|
6
6
|
const validate = require('./validate-pure');
|
|
7
7
|
const render = require('./render');
|
|
8
|
+
const compose = require('./compose');
|
|
8
9
|
|
|
9
10
|
module.exports = {
|
|
10
11
|
...loader,
|
|
11
12
|
...lint,
|
|
12
13
|
...validate,
|
|
13
14
|
...render,
|
|
15
|
+
...compose,
|
|
14
16
|
};
|
package/src/index.mjs
CHANGED
|
@@ -15,3 +15,5 @@ export { lintDomain } from './lint-pure.js';
|
|
|
15
15
|
export { validateDomainSchema, validateCrossFile } from './validate-pure.js';
|
|
16
16
|
|
|
17
17
|
export { renderPreviewHTML, escHtml, renderCard } from './render.js';
|
|
18
|
+
|
|
19
|
+
export { composeContext, classifySignals, composeChecks, loadAndCompose } from './compose.js';
|
package/src/lint-pure.js
CHANGED
|
@@ -168,6 +168,25 @@ function lintDomain(dataMap) {
|
|
|
168
168
|
(pat.self_check || []).forEach((s, i) => yesno(s, `KDNA_Patterns.json.self_check[${i}]`));
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
// Anti-vagueness: axioms must be domain-specific, not generic platitudes
|
|
172
|
+
if (core && Array.isArray(core.axioms)) {
|
|
173
|
+
const vaguePhrases = [
|
|
174
|
+
'be helpful', 'be professional', 'be accurate', 'best practices',
|
|
175
|
+
'user-centric', 'customer-focused', 'excellence', 'innovation',
|
|
176
|
+
'以人为本', '用户至上', '最佳实践', '卓越',
|
|
177
|
+
];
|
|
178
|
+
core.axioms.forEach((a, i) => {
|
|
179
|
+
const text = ((a.one_sentence || '') + ' ' + (a.full_statement || '')).toLowerCase();
|
|
180
|
+
vaguePhrases.forEach((phrase) => {
|
|
181
|
+
if (text.includes(phrase)) {
|
|
182
|
+
warnings.push(
|
|
183
|
+
`KDNA_Core.json.axioms[${i}]: axiom contains vague phrase "${phrase}" — axioms must be testable and domain-specific, not generic platitudes`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
171
190
|
// Validate KDNA_Reasoning.json
|
|
172
191
|
const rea = dataMap['KDNA_Reasoning.json'];
|
|
173
192
|
if (rea) {
|
package/src/types.d.ts
CHANGED
|
@@ -252,3 +252,9 @@ export function validateCrossFile(dataMap: KDNAFileDataMap): ValidationResult;
|
|
|
252
252
|
export function renderPreviewHTML(domain: LoadedDomain, manifest?: KDNAManifest): string;
|
|
253
253
|
export function escHtml(s: string): string;
|
|
254
254
|
export function renderCard(title: string, count: number | undefined, items: string): string;
|
|
255
|
+
|
|
256
|
+
// Compose
|
|
257
|
+
export function composeContext(domains: LoadedDomain[], options?: { separator?: string }): string;
|
|
258
|
+
export function classifySignals(input: string, domains: Array<{ id: string; core: { trigger_signals?: string[] } }>): number[];
|
|
259
|
+
export function composeChecks(domains: Array<{ id: string; core: { meta: { domain: string } }; patterns: { self_check: string[] } }>): string[];
|
|
260
|
+
export function loadAndCompose(dataMaps: KDNAFileDataMap[], options?: LoadOptions & { separator?: string }): { domains: LoadedDomain[]; context: string; activeIndices: number[] };
|