@aikdna/kdna-core 0.1.0 → 0.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikdna/kdna-core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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/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[] };