noormme 1.2.0 → 1.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/README.md +60 -6
- package/dist/cjs/agentic/ActionJournal.d.ts +5 -2
- package/dist/cjs/agentic/ActionJournal.js +13 -5
- package/dist/cjs/agentic/CapabilityManager.d.ts +7 -0
- package/dist/cjs/agentic/CapabilityManager.js +84 -7
- package/dist/cjs/agentic/CognitiveRepository.js +3 -6
- package/dist/cjs/agentic/Cortex.d.ts +4 -0
- package/dist/cjs/agentic/Cortex.js +38 -17
- package/dist/cjs/agentic/EpisodicMemory.d.ts +5 -1
- package/dist/cjs/agentic/EpisodicMemory.js +11 -4
- package/dist/cjs/agentic/PersonaManager.js +37 -31
- package/dist/cjs/agentic/PolicyEnforcer.d.ts +6 -1
- package/dist/cjs/agentic/PolicyEnforcer.js +74 -17
- package/dist/cjs/agentic/ResourceMonitor.d.ts +9 -0
- package/dist/cjs/agentic/ResourceMonitor.js +36 -2
- package/dist/cjs/agentic/SessionManager.js +24 -17
- package/dist/cjs/agentic/VectorIndexer.d.ts +1 -0
- package/dist/cjs/agentic/VectorIndexer.js +26 -17
- package/dist/cjs/agentic/improvement/AblationEngine.d.ts +4 -6
- package/dist/cjs/agentic/improvement/AblationEngine.js +57 -37
- package/dist/cjs/agentic/improvement/ActionRefiner.js +30 -14
- package/dist/cjs/agentic/improvement/ConflictResolver.d.ts +3 -1
- package/dist/cjs/agentic/improvement/ConflictResolver.js +59 -47
- package/dist/cjs/agentic/improvement/CortexJanitor.js +11 -0
- package/dist/cjs/agentic/improvement/CuriosityEngine.d.ts +1 -1
- package/dist/cjs/agentic/improvement/CuriosityEngine.js +48 -21
- package/dist/cjs/agentic/improvement/EvolutionRitual.js +26 -14
- package/dist/cjs/agentic/improvement/EvolutionaryPilot.js +16 -4
- package/dist/cjs/agentic/improvement/GoalArchitect.d.ts +6 -2
- package/dist/cjs/agentic/improvement/GoalArchitect.js +72 -34
- package/dist/cjs/agentic/improvement/GovernanceManager.d.ts +9 -3
- package/dist/cjs/agentic/improvement/GovernanceManager.js +232 -92
- package/dist/cjs/agentic/improvement/HiveLink.d.ts +7 -3
- package/dist/cjs/agentic/improvement/HiveLink.js +135 -113
- package/dist/cjs/agentic/improvement/KnowledgeDistiller.js +43 -35
- package/dist/cjs/agentic/improvement/QuotaManager.d.ts +41 -0
- package/dist/cjs/agentic/improvement/QuotaManager.js +185 -0
- package/dist/cjs/agentic/improvement/RecursiveReasoner.js +50 -25
- package/dist/cjs/agentic/improvement/ReflectionEngine.d.ts +4 -1
- package/dist/cjs/agentic/improvement/ReflectionEngine.js +3 -1
- package/dist/cjs/agentic/improvement/RitualOrchestrator.js +27 -16
- package/dist/cjs/agentic/improvement/RuleEngine.d.ts +1 -1
- package/dist/cjs/agentic/improvement/RuleEngine.js +10 -4
- package/dist/cjs/agentic/improvement/SelfEvolution.js +21 -17
- package/dist/cjs/agentic/improvement/SelfTestRegistry.d.ts +5 -0
- package/dist/cjs/agentic/improvement/SelfTestRegistry.js +129 -109
- package/dist/cjs/agentic/improvement/SkillSynthesizer.d.ts +1 -0
- package/dist/cjs/agentic/improvement/SkillSynthesizer.js +22 -13
- package/dist/cjs/agentic/improvement/StrategicPlanner.d.ts +1 -0
- package/dist/cjs/agentic/improvement/StrategicPlanner.js +26 -19
- package/dist/cjs/agentic/telemetry/CognitiveSynthesizer.d.ts +5 -0
- package/dist/cjs/agentic/telemetry/CognitiveSynthesizer.js +54 -12
- package/dist/cjs/agentic/telemetry/EventHarvester.d.ts +1 -1
- package/dist/cjs/agentic/telemetry/EventHarvester.js +10 -3
- package/dist/cjs/agentic/telemetry/ResearchAlchemist.d.ts +7 -2
- package/dist/cjs/agentic/telemetry/ResearchAlchemist.js +49 -8
- package/dist/cjs/agentic/telemetry/TelemetryOrchestrator.d.ts +4 -1
- package/dist/cjs/agentic/telemetry/TelemetryOrchestrator.js +38 -11
- package/dist/cjs/cli/commands/inspect.js +40 -1
- package/dist/cjs/cli/commands/watch.js +31 -25
- package/dist/cjs/dialect/sqlite/sqlite-introspector.js +15 -5
- package/dist/cjs/helpers/agent-schema.js +1 -0
- package/dist/cjs/migration/data_migrator.js +4 -4
- package/dist/cjs/migration/schema_differ.js +37 -15
- package/dist/cjs/types/index.d.ts +12 -0
- package/dist/cjs/util/safe-sql-helpers.js +7 -10
- package/dist/esm/agentic/ActionJournal.d.ts +5 -2
- package/dist/esm/agentic/ActionJournal.js +13 -5
- package/dist/esm/agentic/CapabilityManager.d.ts +7 -0
- package/dist/esm/agentic/CapabilityManager.js +84 -7
- package/dist/esm/agentic/CognitiveRepository.js +3 -6
- package/dist/esm/agentic/Cortex.d.ts +4 -0
- package/dist/esm/agentic/Cortex.js +38 -17
- package/dist/esm/agentic/EpisodicMemory.d.ts +5 -1
- package/dist/esm/agentic/EpisodicMemory.js +11 -4
- package/dist/esm/agentic/PersonaManager.js +37 -31
- package/dist/esm/agentic/PolicyEnforcer.d.ts +6 -1
- package/dist/esm/agentic/PolicyEnforcer.js +74 -17
- package/dist/esm/agentic/ResourceMonitor.d.ts +9 -0
- package/dist/esm/agentic/ResourceMonitor.js +36 -2
- package/dist/esm/agentic/SessionManager.js +24 -17
- package/dist/esm/agentic/VectorIndexer.d.ts +1 -0
- package/dist/esm/agentic/VectorIndexer.js +26 -17
- package/dist/esm/agentic/improvement/AblationEngine.d.ts +4 -6
- package/dist/esm/agentic/improvement/AblationEngine.js +57 -37
- package/dist/esm/agentic/improvement/ActionRefiner.js +30 -14
- package/dist/esm/agentic/improvement/ConflictResolver.d.ts +3 -1
- package/dist/esm/agentic/improvement/ConflictResolver.js +59 -47
- package/dist/esm/agentic/improvement/CortexJanitor.js +11 -0
- package/dist/esm/agentic/improvement/CuriosityEngine.d.ts +1 -1
- package/dist/esm/agentic/improvement/CuriosityEngine.js +48 -21
- package/dist/esm/agentic/improvement/EvolutionRitual.js +26 -14
- package/dist/esm/agentic/improvement/EvolutionaryPilot.js +16 -4
- package/dist/esm/agentic/improvement/GoalArchitect.d.ts +6 -2
- package/dist/esm/agentic/improvement/GoalArchitect.js +72 -34
- package/dist/esm/agentic/improvement/GovernanceManager.d.ts +9 -3
- package/dist/esm/agentic/improvement/GovernanceManager.js +232 -92
- package/dist/esm/agentic/improvement/HiveLink.d.ts +7 -3
- package/dist/esm/agentic/improvement/HiveLink.js +135 -113
- package/dist/esm/agentic/improvement/KnowledgeDistiller.js +43 -35
- package/dist/esm/agentic/improvement/QuotaManager.d.ts +41 -0
- package/dist/esm/agentic/improvement/QuotaManager.js +182 -0
- package/dist/esm/agentic/improvement/RecursiveReasoner.js +50 -25
- package/dist/esm/agentic/improvement/ReflectionEngine.d.ts +4 -1
- package/dist/esm/agentic/improvement/ReflectionEngine.js +3 -1
- package/dist/esm/agentic/improvement/RitualOrchestrator.js +27 -16
- package/dist/esm/agentic/improvement/RuleEngine.d.ts +1 -1
- package/dist/esm/agentic/improvement/RuleEngine.js +10 -4
- package/dist/esm/agentic/improvement/SelfEvolution.js +21 -17
- package/dist/esm/agentic/improvement/SelfTestRegistry.d.ts +5 -0
- package/dist/esm/agentic/improvement/SelfTestRegistry.js +129 -109
- package/dist/esm/agentic/improvement/SkillSynthesizer.d.ts +1 -0
- package/dist/esm/agentic/improvement/SkillSynthesizer.js +22 -13
- package/dist/esm/agentic/improvement/StrategicPlanner.d.ts +1 -0
- package/dist/esm/agentic/improvement/StrategicPlanner.js +26 -19
- package/dist/esm/agentic/telemetry/CognitiveSynthesizer.d.ts +5 -0
- package/dist/esm/agentic/telemetry/CognitiveSynthesizer.js +54 -12
- package/dist/esm/agentic/telemetry/EventHarvester.d.ts +1 -1
- package/dist/esm/agentic/telemetry/EventHarvester.js +10 -3
- package/dist/esm/agentic/telemetry/ResearchAlchemist.d.ts +7 -2
- package/dist/esm/agentic/telemetry/ResearchAlchemist.js +49 -8
- package/dist/esm/agentic/telemetry/TelemetryOrchestrator.d.ts +4 -1
- package/dist/esm/agentic/telemetry/TelemetryOrchestrator.js +38 -11
- package/dist/esm/cli/commands/inspect.js +40 -1
- package/dist/esm/cli/commands/watch.js +31 -25
- package/dist/esm/dialect/sqlite/sqlite-introspector.js +15 -5
- package/dist/esm/helpers/agent-schema.js +1 -0
- package/dist/esm/migration/data_migrator.js +4 -4
- package/dist/esm/migration/schema_differ.js +37 -15
- package/dist/esm/types/index.d.ts +12 -0
- package/dist/esm/util/safe-sql-helpers.js +7 -10
- package/package.json +1 -1
|
@@ -22,62 +22,69 @@ export class HiveLink {
|
|
|
22
22
|
* Promote high-confidence local knowledge to global "Hive" knowledge.
|
|
23
23
|
* This creates a new, session-agnostic entry or updates an existing global one.
|
|
24
24
|
*/
|
|
25
|
-
async broadcastKnowledge(
|
|
26
|
-
|
|
25
|
+
async broadcastKnowledge(options = {}) {
|
|
26
|
+
const minConfidence = options.minConfidence ?? 0.9;
|
|
27
|
+
const limit = options.limit ?? 100;
|
|
28
|
+
const offset = options.offset ?? 0;
|
|
29
|
+
console.log(`[HiveLink] Broadcasting knowledge (limit=${limit}, offset=${offset}) with confidence >= ${minConfidence}`);
|
|
27
30
|
// Find high-confidence items that are "local" (have a source_session_id)
|
|
28
31
|
const items = (await this.typedDb
|
|
29
32
|
.selectFrom(this.knowledgeTable)
|
|
30
33
|
.selectAll()
|
|
31
34
|
.where('confidence', '>=', minConfidence)
|
|
32
35
|
.where('source_session_id', 'is not', null) // Only local items
|
|
36
|
+
.limit(limit)
|
|
37
|
+
.offset(offset)
|
|
33
38
|
.execute());
|
|
34
39
|
let promotedCount = 0;
|
|
35
40
|
for (const item of items) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
tags.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
41
|
+
await this.db.transaction().execute(async (trx) => {
|
|
42
|
+
// Check if a global version already exists
|
|
43
|
+
const existingGlobal = await trx
|
|
44
|
+
.selectFrom(this.knowledgeTable)
|
|
45
|
+
.selectAll()
|
|
46
|
+
.where('entity', '=', item.entity)
|
|
47
|
+
.where('fact', '=', item.fact)
|
|
48
|
+
.where('source_session_id', 'is', null)
|
|
49
|
+
.forUpdate() // Audit Phase 15: prevent concurrent promotion duplication
|
|
50
|
+
.executeTakeFirst();
|
|
51
|
+
if (existingGlobal) {
|
|
52
|
+
// Reinforce existing global knowledge
|
|
53
|
+
const newConfidence = Math.min(0.99, Math.max(existingGlobal.confidence, item.confidence) + 0.01);
|
|
54
|
+
await trx
|
|
55
|
+
.updateTable(this.knowledgeTable)
|
|
56
|
+
.set({
|
|
57
|
+
confidence: newConfidence,
|
|
58
|
+
updated_at: new Date(),
|
|
59
|
+
})
|
|
60
|
+
.where('id', '=', existingGlobal.id)
|
|
61
|
+
.execute();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Create new global knowledge
|
|
65
|
+
const tags = item.tags ? [...item.tags] : [];
|
|
66
|
+
if (!tags.includes('hive_mind'))
|
|
67
|
+
tags.push('hive_mind');
|
|
68
|
+
await trx
|
|
69
|
+
.insertInto(this.knowledgeTable)
|
|
70
|
+
.values({
|
|
71
|
+
entity: item.entity,
|
|
72
|
+
fact: item.fact,
|
|
73
|
+
confidence: item.confidence,
|
|
74
|
+
source_session_id: null,
|
|
75
|
+
tags: JSON.stringify(tags),
|
|
76
|
+
metadata: JSON.stringify({
|
|
77
|
+
...item.metadata,
|
|
78
|
+
promoted_from: item.id,
|
|
79
|
+
promoted_at: new Date(),
|
|
80
|
+
}),
|
|
81
|
+
created_at: new Date(),
|
|
82
|
+
updated_at: new Date(),
|
|
83
|
+
})
|
|
84
|
+
.execute();
|
|
85
|
+
promotedCount++;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
81
88
|
}
|
|
82
89
|
return promotedCount;
|
|
83
90
|
}
|
|
@@ -102,8 +109,8 @@ export class HiveLink {
|
|
|
102
109
|
return Number(result[0]?.numUpdatedRows ?? 1);
|
|
103
110
|
}
|
|
104
111
|
/**
|
|
105
|
-
* Propagate high-performing capabilities globally
|
|
106
|
-
*
|
|
112
|
+
* Propagate high-performing capabilities globally using the "Sovereign Draft" protocol.
|
|
113
|
+
* Uses Bayesian Convergence and Shadow Promotion to avoid deadlocks.
|
|
107
114
|
*/
|
|
108
115
|
async broadcastSkills() {
|
|
109
116
|
if (!this.config.evolution?.enableHiveLink &&
|
|
@@ -111,81 +118,96 @@ export class HiveLink {
|
|
|
111
118
|
console.log('[HiveLink] Skill broadcasting disabled by config.');
|
|
112
119
|
return 0;
|
|
113
120
|
}
|
|
114
|
-
console.log(`[HiveLink]
|
|
121
|
+
console.log(`[HiveLink] Executing Sovereign Draft for emergent skills...`);
|
|
115
122
|
let broadcastCount = 0;
|
|
116
123
|
const capTable = this.config.capabilitiesTable || 'agent_capabilities';
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
// 1. Resolve Verified Skills with Bayesian "Alpha" selection
|
|
125
|
+
const verifiedSkills = await this.cortex.capabilities.getCapabilities('verified');
|
|
126
|
+
// Group verified skills by lineage (base tool name)
|
|
127
|
+
const lineageGroups = new Map();
|
|
128
|
+
for (const skill of verifiedSkills) {
|
|
129
|
+
const meta = typeof skill.metadata === 'string' ? JSON.parse(skill.metadata) : (skill.metadata || {});
|
|
130
|
+
const lineage = meta.lineage || skill.name;
|
|
131
|
+
if (!lineageGroups.has(lineage))
|
|
132
|
+
lineageGroups.set(lineage, []);
|
|
133
|
+
lineageGroups.get(lineage).push({ skill, meta });
|
|
134
|
+
}
|
|
135
|
+
for (const [lineage, variants] of lineageGroups.entries()) {
|
|
136
|
+
// Bayesian Winner Selection
|
|
137
|
+
// Score = (anchored_reliability * totalCount + K * prior) / (totalCount + K)
|
|
138
|
+
// For simplicity, we use anchored_reliability which already incorporates this weighting
|
|
139
|
+
const alphaCandidate = variants.reduce((prev, curr) => {
|
|
140
|
+
const pAnchored = prev.meta.anchored_reliability ?? 0;
|
|
141
|
+
const cAnchored = curr.meta.anchored_reliability ?? 0;
|
|
142
|
+
return cAnchored > pAnchored ? curr : prev;
|
|
143
|
+
});
|
|
144
|
+
// Non-Blocking Set Update: Flag Alpha and Shadow versions
|
|
145
|
+
await this.db.transaction().execute(async (trx) => {
|
|
146
|
+
const currentMeta = alphaCandidate.meta || {};
|
|
147
|
+
const updatedMeta = {
|
|
148
|
+
...currentMeta,
|
|
149
|
+
is_alpha: true,
|
|
150
|
+
broadcasted: true,
|
|
151
|
+
broadcasted_at: new Date()
|
|
152
|
+
};
|
|
153
|
+
// Flag the winner as Alpha
|
|
154
|
+
// Audit Phase 15: Lock candidate before promotion
|
|
155
|
+
const winner = await trx
|
|
129
156
|
.selectFrom(capTable)
|
|
130
|
-
.
|
|
131
|
-
.where('
|
|
132
|
-
.
|
|
133
|
-
.where('id', '!=', skill.id)
|
|
157
|
+
.select('id')
|
|
158
|
+
.where('id', '=', alphaCandidate.skill.id)
|
|
159
|
+
.forUpdate()
|
|
134
160
|
.executeTakeFirst();
|
|
135
|
-
|
|
136
|
-
if (competitor) {
|
|
137
|
-
const comp = competitor;
|
|
138
|
-
const compRel = comp.reliability || 0;
|
|
139
|
-
// Performance-Based Conflict Resolution: Only broadcast if reliability is strictly better
|
|
140
|
-
// or if it's a direct version upgrade with equal/better reliability
|
|
141
|
-
const isNewer = this.compareVersions(skill.version, comp.version) > 0;
|
|
142
|
-
if (compRel > skill.reliability) {
|
|
143
|
-
shouldBroadcast = false;
|
|
144
|
-
}
|
|
145
|
-
else if (compRel === skill.reliability && !isNewer) {
|
|
146
|
-
shouldBroadcast = false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if (shouldBroadcast) {
|
|
161
|
+
if (winner) {
|
|
150
162
|
await trx
|
|
151
163
|
.updateTable(capTable)
|
|
152
164
|
.set({
|
|
153
|
-
metadata: JSON.stringify(
|
|
154
|
-
...meta,
|
|
155
|
-
broadcasted: true,
|
|
156
|
-
hive_verified: true,
|
|
157
|
-
broadcasted_at: new Date(),
|
|
158
|
-
conflict_resolved: !!competitor,
|
|
159
|
-
}),
|
|
165
|
+
metadata: JSON.stringify(updatedMeta),
|
|
160
166
|
})
|
|
161
|
-
.where('id', '=',
|
|
167
|
+
.where('id', '=', winner.id)
|
|
162
168
|
.execute();
|
|
163
|
-
broadcastCount++;
|
|
164
169
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
...
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
.where('id', '=', skill.id)
|
|
184
|
-
.execute();
|
|
185
|
-
broadcastCount++;
|
|
170
|
+
// Flag others in the same lineage as Shadow (Dialect-safe approach)
|
|
171
|
+
const shadowIds = variants
|
|
172
|
+
.filter(v => v.skill.id !== alphaCandidate.skill.id)
|
|
173
|
+
.map(v => v.skill.id);
|
|
174
|
+
if (shadowIds.length > 0) {
|
|
175
|
+
// We fetch and update individually or use a bulk update if the dialect supports complex JSON manipulation.
|
|
176
|
+
// For maximum compatibility (Postgres/SQLite), we do it in a loop for the shadows if the count is small.
|
|
177
|
+
for (const sid of shadowIds) {
|
|
178
|
+
const sMatch = variants.find(v => v.skill.id === sid);
|
|
179
|
+
const sMeta = sMatch?.meta || {};
|
|
180
|
+
await trx.updateTable(capTable)
|
|
181
|
+
.set({
|
|
182
|
+
metadata: JSON.stringify({ ...sMeta, is_alpha: false, is_shadow: true }),
|
|
183
|
+
status: 'experimental'
|
|
184
|
+
})
|
|
185
|
+
.where('id', '=', sid)
|
|
186
|
+
.execute();
|
|
187
|
+
}
|
|
186
188
|
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
+
});
|
|
190
|
+
broadcastCount += variants.length;
|
|
191
|
+
}
|
|
192
|
+
// 2. Broadcast Blacklisted Skills (Immune Prophet)
|
|
193
|
+
const blacklisted = await this.cortex.capabilities.getCapabilities('blacklisted');
|
|
194
|
+
const blackIDs = blacklisted
|
|
195
|
+
.filter(s => {
|
|
196
|
+
const meta = typeof s.metadata === 'string' ? JSON.parse(s.metadata) : (s.metadata || {});
|
|
197
|
+
return !meta.broadcasted;
|
|
198
|
+
})
|
|
199
|
+
.map(s => s.id);
|
|
200
|
+
if (blackIDs.length > 0) {
|
|
201
|
+
await this.db
|
|
202
|
+
.updateTable(capTable)
|
|
203
|
+
.set({
|
|
204
|
+
metadata: sql `json_set(metadata, '$.broadcasted', true, '$.hive_blacklisted', true)`,
|
|
205
|
+
updated_at: new Date()
|
|
206
|
+
})
|
|
207
|
+
.where('id', 'in', blackIDs)
|
|
208
|
+
.execute();
|
|
209
|
+
broadcastCount += blackIDs.length;
|
|
210
|
+
}
|
|
189
211
|
return broadcastCount;
|
|
190
212
|
}
|
|
191
213
|
/**
|
|
@@ -101,6 +101,7 @@ export class KnowledgeDistiller {
|
|
|
101
101
|
return this.parseKnowledge(updated);
|
|
102
102
|
}
|
|
103
103
|
// Conflict Detection: Check if a similar entity has a conflicting fact
|
|
104
|
+
// Audit Phase 11: Explicit transaction propagation
|
|
104
105
|
await this.challengeKnowledge(entity, fact, confidence, trx);
|
|
105
106
|
// Create new
|
|
106
107
|
const initialMeta = {
|
|
@@ -195,35 +196,38 @@ export class KnowledgeDistiller {
|
|
|
195
196
|
* Record a retrieval hit for a knowledge item.
|
|
196
197
|
*/
|
|
197
198
|
async recordHit(id) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
199
|
+
await this.db.transaction().execute(async (trx) => {
|
|
200
|
+
const existing = await trx
|
|
201
|
+
.selectFrom(this.knowledgeTable)
|
|
202
|
+
.selectAll()
|
|
203
|
+
.where('id', '=', id)
|
|
204
|
+
.forUpdate() // Audit Phase 11: Atomic hit record
|
|
205
|
+
.executeTakeFirst();
|
|
206
|
+
if (!existing)
|
|
207
|
+
return;
|
|
208
|
+
const metadata = typeof existing.metadata === 'string'
|
|
209
|
+
? JSON.parse(existing.metadata)
|
|
210
|
+
: existing.metadata || {};
|
|
211
|
+
metadata.hit_count = (metadata.hit_count || 0) + 1;
|
|
212
|
+
metadata.last_retrieved_at = new Date().toISOString();
|
|
213
|
+
await trx
|
|
214
|
+
.updateTable(this.knowledgeTable)
|
|
215
|
+
.set({
|
|
216
|
+
metadata: JSON.stringify(metadata),
|
|
217
|
+
updated_at: new Date(),
|
|
218
|
+
})
|
|
219
|
+
.where('id', '=', id)
|
|
220
|
+
.execute();
|
|
221
|
+
// Production Hardening: Emit a metric event for this hit to enable efficient hotspot detection
|
|
222
|
+
await trx
|
|
223
|
+
.insertInto(this.config.metricsTable || 'agent_metrics')
|
|
224
|
+
.values({
|
|
225
|
+
metric_name: `entity_hit_${existing.entity}`,
|
|
226
|
+
metric_value: 1,
|
|
227
|
+
created_at: new Date(),
|
|
228
|
+
})
|
|
229
|
+
.execute();
|
|
230
|
+
});
|
|
227
231
|
}
|
|
228
232
|
/**
|
|
229
233
|
* Calculate the "Fitness" of a memory item.
|
|
@@ -246,6 +250,8 @@ export class KnowledgeDistiller {
|
|
|
246
250
|
* we degrade the confidence of the old facts.
|
|
247
251
|
*/
|
|
248
252
|
async challengeKnowledge(entity, competingFact, confidence, trxOrDb = this.db) {
|
|
253
|
+
// Audit Phase 11: Semantic sanitization of competing fact
|
|
254
|
+
const safeFact = competingFact.slice(0, 500).replace(/[\u0000-\u001F\u007F-\u009F]/g, '').replace(/<\|.*?\|>/g, '');
|
|
249
255
|
const query = trxOrDb
|
|
250
256
|
.selectFrom(this.knowledgeTable)
|
|
251
257
|
.selectAll()
|
|
@@ -267,7 +273,7 @@ export class KnowledgeDistiller {
|
|
|
267
273
|
newStatus = 'disputed';
|
|
268
274
|
newMeta = {
|
|
269
275
|
...newMeta,
|
|
270
|
-
status_reason: `Contradicted by: ${
|
|
276
|
+
status_reason: `Contradicted by: ${safeFact}`,
|
|
271
277
|
};
|
|
272
278
|
penalty = 0.1;
|
|
273
279
|
}
|
|
@@ -275,7 +281,7 @@ export class KnowledgeDistiller {
|
|
|
275
281
|
newStatus = 'deprecated';
|
|
276
282
|
newMeta = {
|
|
277
283
|
...newMeta,
|
|
278
|
-
status_reason: `Superseded by: ${
|
|
284
|
+
status_reason: `Superseded by: ${safeFact}`,
|
|
279
285
|
};
|
|
280
286
|
penalty = 0.4;
|
|
281
287
|
}
|
|
@@ -416,22 +422,24 @@ export class KnowledgeDistiller {
|
|
|
416
422
|
*/
|
|
417
423
|
async consolidateKnowledge() {
|
|
418
424
|
let totalMerged = 0;
|
|
419
|
-
// Find entities with multiple facts
|
|
425
|
+
// Find entities with multiple facts (Paginated to handle scale)
|
|
420
426
|
const candidates = await this.db
|
|
421
427
|
.selectFrom(this.knowledgeTable)
|
|
422
428
|
.select('entity')
|
|
423
429
|
.groupBy('entity')
|
|
424
430
|
.having((eb) => eb.fn.count('id'), '>', 1)
|
|
431
|
+
.limit(500) // Audit Phase 11: Chunked search
|
|
425
432
|
.execute();
|
|
426
433
|
for (const cand of candidates) {
|
|
427
434
|
const entity = cand.entity;
|
|
428
435
|
const items = await this.getKnowledgeByEntity(entity);
|
|
429
|
-
// Iterative merging inside the entity bucket
|
|
436
|
+
// Iterative merging inside the entity bucket (Paginated check)
|
|
430
437
|
const mergedIds = new Set();
|
|
431
|
-
|
|
438
|
+
const iterLimit = Math.min(items.length, 100);
|
|
439
|
+
for (let i = 0; i < iterLimit; i++) {
|
|
432
440
|
if (mergedIds.has(items[i].id))
|
|
433
441
|
continue;
|
|
434
|
-
for (let j = i + 1; j <
|
|
442
|
+
for (let j = i + 1; j < iterLimit; j++) {
|
|
435
443
|
if (mergedIds.has(items[j].id))
|
|
436
444
|
continue;
|
|
437
445
|
const sim = calculateSimilarity(items[i].fact, items[j].fact);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Kysely } from '../../kysely.js';
|
|
2
|
+
import type { AgenticConfig } from '../../types/index.js';
|
|
3
|
+
import type { Cortex } from '../Cortex.js';
|
|
4
|
+
/**
|
|
5
|
+
* QuotaManager enforces resource limits across personas and swarms.
|
|
6
|
+
*/
|
|
7
|
+
export declare class QuotaManager {
|
|
8
|
+
private db;
|
|
9
|
+
private cortex;
|
|
10
|
+
private config;
|
|
11
|
+
private resourcesTable;
|
|
12
|
+
private metricsTable;
|
|
13
|
+
private rateCache;
|
|
14
|
+
private readonly CACHE_TTL;
|
|
15
|
+
constructor(db: Kysely<any>, cortex: Cortex, config?: AgenticConfig);
|
|
16
|
+
/**
|
|
17
|
+
* Check if a quota is violated for a given target.
|
|
18
|
+
*/
|
|
19
|
+
checkQuota(targetType: 'persona' | 'swarm' | 'global', targetId?: string | null): Promise<{
|
|
20
|
+
allowed: boolean;
|
|
21
|
+
reason?: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Fetches active quotas for a given target.
|
|
25
|
+
*/
|
|
26
|
+
private getActiveQuotas;
|
|
27
|
+
/**
|
|
28
|
+
* Calculates current usage for a specific quota window.
|
|
29
|
+
*/
|
|
30
|
+
private getCurrentUsage;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves exchange rate via persistent metric store or live update.
|
|
33
|
+
* Implements a true Oracle pattern for resource normalization.
|
|
34
|
+
*/
|
|
35
|
+
private getExchangeRate;
|
|
36
|
+
/**
|
|
37
|
+
* Manually sync exchange rates from an external provider (Oracle pulse).
|
|
38
|
+
*/
|
|
39
|
+
syncExchangeRates(rates: Record<string, number>): Promise<void>;
|
|
40
|
+
private getPeriodInMs;
|
|
41
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/// <reference types="./QuotaManager.d.ts" />
|
|
2
|
+
import { sql } from '../../raw-builder/sql.js';
|
|
3
|
+
/**
|
|
4
|
+
* QuotaManager enforces resource limits across personas and swarms.
|
|
5
|
+
*/
|
|
6
|
+
export class QuotaManager {
|
|
7
|
+
db;
|
|
8
|
+
cortex;
|
|
9
|
+
config;
|
|
10
|
+
resourcesTable;
|
|
11
|
+
metricsTable;
|
|
12
|
+
rateCache = new Map();
|
|
13
|
+
CACHE_TTL = 1000 * 60 * 60; // 1 hour cache
|
|
14
|
+
constructor(db, cortex, config = {}) {
|
|
15
|
+
this.db = db;
|
|
16
|
+
this.cortex = cortex;
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.resourcesTable = config.resourcesTable || 'agent_resource_usage';
|
|
19
|
+
this.metricsTable = config.metricsTable || 'agent_metrics';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if a quota is violated for a given target.
|
|
23
|
+
*/
|
|
24
|
+
async checkQuota(targetType, targetId = null) {
|
|
25
|
+
const quotas = await this.getActiveQuotas(targetType, targetId);
|
|
26
|
+
for (const quota of quotas) {
|
|
27
|
+
const usage = await this.getCurrentUsage(quota);
|
|
28
|
+
// Currency Conversion Logic: Convert usage to target currency if specified
|
|
29
|
+
let normalizedUsage = usage;
|
|
30
|
+
if (quota.metric === 'cost' && quota.metadata?.currency && quota.metadata.currency !== 'USD') {
|
|
31
|
+
const rate = await this.getExchangeRate(quota.metadata.currency);
|
|
32
|
+
normalizedUsage = usage * rate;
|
|
33
|
+
}
|
|
34
|
+
if (normalizedUsage >= quota.limit) {
|
|
35
|
+
const limitStr = quota.metric === 'cost'
|
|
36
|
+
? `${quota.metadata?.currency || '$'}${quota.limit}`
|
|
37
|
+
: `${quota.limit}`;
|
|
38
|
+
return {
|
|
39
|
+
allowed: false,
|
|
40
|
+
reason: `Quota exceeded: ${quota.metric} limit is ${limitStr}, current is ${normalizedUsage.toFixed(4)} (${quota.period})`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { allowed: true };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Fetches active quotas for a given target.
|
|
48
|
+
*/
|
|
49
|
+
async getActiveQuotas(targetType, targetId) {
|
|
50
|
+
const quotas = [];
|
|
51
|
+
// 1. Fetch from PolicyEnforcer (Global & Type-specific)
|
|
52
|
+
const policies = await this.cortex.policies.getActivePolicies();
|
|
53
|
+
for (const policy of policies) {
|
|
54
|
+
if (policy.type === 'budget' &&
|
|
55
|
+
(policy.definition.targetType === targetType ||
|
|
56
|
+
(!policy.definition.targetType && targetType === 'global'))) {
|
|
57
|
+
quotas.push({
|
|
58
|
+
id: policy.id,
|
|
59
|
+
targetType: targetType,
|
|
60
|
+
targetId: targetId,
|
|
61
|
+
metric: policy.definition.metric || 'cost',
|
|
62
|
+
limit: policy.definition.limit || 0,
|
|
63
|
+
period: policy.definition.period || 'hourly',
|
|
64
|
+
currentUsage: 0,
|
|
65
|
+
createdAt: policy.createdAt,
|
|
66
|
+
updatedAt: policy.updatedAt,
|
|
67
|
+
metadata: policy.metadata
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// 2. Fetch from Persona metadata (Specific override)
|
|
72
|
+
if (targetType === 'persona' && targetId) {
|
|
73
|
+
const persona = await this.db
|
|
74
|
+
.selectFrom(this.config.personasTable || 'agent_personas')
|
|
75
|
+
.select('metadata')
|
|
76
|
+
.where('id', '=', targetId)
|
|
77
|
+
.executeTakeFirst();
|
|
78
|
+
if (persona) {
|
|
79
|
+
const meta = typeof persona.metadata === 'string'
|
|
80
|
+
? JSON.parse(persona.metadata)
|
|
81
|
+
: persona.metadata || {};
|
|
82
|
+
if (meta.quotas) {
|
|
83
|
+
quotas.push(...meta.quotas);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return quotas;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Calculates current usage for a specific quota window.
|
|
91
|
+
*/
|
|
92
|
+
async getCurrentUsage(quota) {
|
|
93
|
+
const windowMs = this.getPeriodInMs(quota.period);
|
|
94
|
+
const startTime = new Date(Date.now() - windowMs);
|
|
95
|
+
let query = this.db
|
|
96
|
+
.selectFrom(this.resourcesTable)
|
|
97
|
+
.where('created_at', '>', startTime);
|
|
98
|
+
if (quota.targetType === 'persona' && quota.targetId) {
|
|
99
|
+
query = query.where('agent_id', '=', quota.targetId);
|
|
100
|
+
}
|
|
101
|
+
else if (quota.targetType === 'swarm' && quota.targetId) {
|
|
102
|
+
// PRODUCTION HARDENING: Efficient JSON filtering
|
|
103
|
+
query = query.where((eb) => eb.or([
|
|
104
|
+
eb(sql `json_extract(metadata, '$.swarm_id')`, '=', quota.targetId),
|
|
105
|
+
eb(sql `metadata->>'swarm_id'`, '=', String(quota.targetId))
|
|
106
|
+
]));
|
|
107
|
+
}
|
|
108
|
+
const metricField = quota.metric === 'cost' ? 'cost' :
|
|
109
|
+
quota.metric === 'tokens_input' ? 'input_tokens' :
|
|
110
|
+
quota.metric === 'tokens_output' ? 'output_tokens' :
|
|
111
|
+
sql `input_tokens + output_tokens`;
|
|
112
|
+
const result = await query
|
|
113
|
+
.select((eb) => eb.fn.sum(metricField).as('total'))
|
|
114
|
+
.executeTakeFirst();
|
|
115
|
+
const usage = Number(result?.total || 0);
|
|
116
|
+
// Audit Phase 7: Floating-point drift protection
|
|
117
|
+
const epsilon = 1e-10;
|
|
118
|
+
return usage < epsilon ? 0 : usage;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Resolves exchange rate via persistent metric store or live update.
|
|
122
|
+
* Implements a true Oracle pattern for resource normalization.
|
|
123
|
+
*/
|
|
124
|
+
async getExchangeRate(currency) {
|
|
125
|
+
// 1. Check in-memory cache
|
|
126
|
+
const cached = this.rateCache.get(currency);
|
|
127
|
+
if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
|
|
128
|
+
return cached.rate;
|
|
129
|
+
}
|
|
130
|
+
// 2. Check persistent metric store
|
|
131
|
+
const record = await this.db
|
|
132
|
+
.selectFrom(this.metricsTable)
|
|
133
|
+
.select(['metric_value', 'created_at'])
|
|
134
|
+
.where('metric_name', '=', `exchange_rate_${currency}`)
|
|
135
|
+
.orderBy('created_at', 'desc')
|
|
136
|
+
.executeTakeFirst();
|
|
137
|
+
if (record && Date.now() - new Date(record.created_at).getTime() < this.CACHE_TTL * 24) {
|
|
138
|
+
const rate = Number(record.metric_value);
|
|
139
|
+
this.rateCache.set(currency, { rate, timestamp: Date.now() });
|
|
140
|
+
return rate;
|
|
141
|
+
}
|
|
142
|
+
// 3. Fallback to base rates if unreachable (Statically hardened)
|
|
143
|
+
const baseRates = {
|
|
144
|
+
'ETH': 0.00032,
|
|
145
|
+
'BTC': 0.000015,
|
|
146
|
+
'EUR': 0.92,
|
|
147
|
+
'GBP': 0.79
|
|
148
|
+
};
|
|
149
|
+
const fallback = baseRates[currency] || 1.0;
|
|
150
|
+
// Log the need for sync
|
|
151
|
+
console.warn(`[QuotaManager] Exchange rate for ${currency} is stale or missing. Using hardened fallback: ${fallback}`);
|
|
152
|
+
return fallback;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Manually sync exchange rates from an external provider (Oracle pulse).
|
|
156
|
+
*/
|
|
157
|
+
async syncExchangeRates(rates) {
|
|
158
|
+
await this.db.transaction().execute(async (trx) => {
|
|
159
|
+
for (const [currency, rate] of Object.entries(rates)) {
|
|
160
|
+
await trx
|
|
161
|
+
.insertInto(this.metricsTable)
|
|
162
|
+
.values({
|
|
163
|
+
metric_name: `exchange_rate_${currency}`,
|
|
164
|
+
metric_value: rate,
|
|
165
|
+
created_at: new Date()
|
|
166
|
+
})
|
|
167
|
+
.execute();
|
|
168
|
+
this.rateCache.set(currency, { rate, timestamp: Date.now() });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
console.log(`[QuotaManager] Synchronized ${Object.keys(rates).length} exchange rates.`);
|
|
172
|
+
}
|
|
173
|
+
getPeriodInMs(period) {
|
|
174
|
+
switch (period) {
|
|
175
|
+
case 'hourly': return 3600000;
|
|
176
|
+
case 'daily': return 86400000;
|
|
177
|
+
case 'monthly': return 86400000 * 30;
|
|
178
|
+
case 'infinite': return Date.now();
|
|
179
|
+
default: return 3600000;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|