rust-kgdb 0.6.4 → 0.6.5
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/examples/core-concepts-demo.ts +50 -35
- package/examples/datalog-example.ts +246 -339
- package/examples/fraud-detection-agent.js +23 -36
- package/examples/hypermind-fraud-underwriter.ts +54 -51
- package/examples/underwriting-agent.js +16 -20
- package/hypermind-agent.js +984 -2643
- package/package.json +1 -1
- package/rust-kgdb-napi.darwin-x64.node +0 -0
package/hypermind-agent.js
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HyperMind Agentic Framework - TypeScript SDK Implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Neuro-Symbolic AI Agent with ZERO hallucination:
|
|
5
|
+
* - All reasoning grounded in Knowledge Graph
|
|
6
|
+
* - Type theory ensures correct tool composition
|
|
7
|
+
* - Proof theory provides full explainability
|
|
8
|
+
* - Category theory enables morphism composition
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
* - TypeId: Hindley-Milner type system with refinement types
|
|
10
|
-
* - LLMPlanner: Natural language to typed tool pipelines
|
|
11
|
-
* - WasmSandbox: Capability-based security with fuel metering
|
|
12
|
-
* - ObjectProxy: gRPC-style tool invocation with audit logging
|
|
13
|
-
* - AgentBuilder: Fluent builder pattern for agent composition
|
|
10
|
+
* Inspired by: https://www.symbolica.ai/blog/beyond-code-mode-agentica
|
|
14
11
|
*
|
|
15
12
|
* @module hypermind-agent
|
|
16
13
|
*/
|
|
17
14
|
|
|
18
|
-
const http = require('http')
|
|
19
|
-
const https = require('https')
|
|
20
15
|
const crypto = require('crypto')
|
|
21
16
|
|
|
22
17
|
// ============================================================================
|
|
@@ -24,8 +19,8 @@ const crypto = require('crypto')
|
|
|
24
19
|
// ============================================================================
|
|
25
20
|
|
|
26
21
|
/**
|
|
27
|
-
* TypeId - Complete type system
|
|
28
|
-
*
|
|
22
|
+
* TypeId - Complete type system ensuring no hallucination
|
|
23
|
+
* Every value has a proof of its type correctness
|
|
29
24
|
*/
|
|
30
25
|
const TypeId = {
|
|
31
26
|
// Base types
|
|
@@ -62,411 +57,239 @@ const TypeId = {
|
|
|
62
57
|
isCompatible: (output, input) => {
|
|
63
58
|
if (output === input) return true
|
|
64
59
|
if (output === 'BindingSet' && input === 'String') return true
|
|
65
|
-
if (output.startsWith('List<') && input === 'String') return true
|
|
60
|
+
if (output.startsWith && output.startsWith('List<') && input === 'String') return true
|
|
66
61
|
return false
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
|
|
70
65
|
// ============================================================================
|
|
71
|
-
//
|
|
66
|
+
// PROOF NODE (Explainable AI - No Hallucination)
|
|
72
67
|
// ============================================================================
|
|
73
68
|
|
|
74
69
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
70
|
+
* ProofNode - Every result has a proof of its derivation
|
|
71
|
+
* This ensures ZERO hallucination - everything traced to KG or rules
|
|
77
72
|
*/
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
name: 'kg.sparql.update',
|
|
97
|
-
input: TypeId.String,
|
|
98
|
-
output: TypeId.Unit,
|
|
99
|
-
description: 'Execute SPARQL UPDATE operations',
|
|
100
|
-
domain: 'knowledge_graph',
|
|
101
|
-
constraints: { readOnly: false }
|
|
102
|
-
},
|
|
103
|
-
'kg.motif.find': {
|
|
104
|
-
name: 'kg.motif.find',
|
|
105
|
-
input: TypeId.String,
|
|
106
|
-
output: TypeId.List('Match'),
|
|
107
|
-
description: 'Find graph motif patterns (fraud rings, cycles)',
|
|
108
|
-
domain: 'graph_analytics',
|
|
109
|
-
patterns: {
|
|
110
|
-
'collusion': '(a)-[claims_with]->(p); (b)-[claims_with]->(p); (a)-[knows]->(b)',
|
|
111
|
-
'circular_payment': '(a)-[paid]->(b); (b)-[paid]->(c); (c)-[paid]->(a)',
|
|
112
|
-
'star_pattern': '(center)-[]->(a); (center)-[]->(b); (center)-[]->(c)'
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
'kg.datalog.infer': {
|
|
116
|
-
name: 'kg.datalog.infer',
|
|
117
|
-
input: TypeId.List('Rule'),
|
|
118
|
-
output: TypeId.List('Fact'),
|
|
119
|
-
description: 'Apply Datalog rules for logical inference',
|
|
120
|
-
domain: 'reasoning',
|
|
121
|
-
prebuiltRules: {
|
|
122
|
-
'potential_collusion': 'Claimants who know each other using same provider',
|
|
123
|
-
'staged_accident': 'Soft tissue claims with high amounts in first 90 days',
|
|
124
|
-
'organized_fraud': 'High prior claims + high provider volume'
|
|
73
|
+
class ProofNode {
|
|
74
|
+
constructor(type, value, justification, children = []) {
|
|
75
|
+
this.id = crypto.randomUUID()
|
|
76
|
+
this.type = type // 'axiom' | 'sparql' | 'rule' | 'inference' | 'embedding'
|
|
77
|
+
this.value = value
|
|
78
|
+
this.justification = justification
|
|
79
|
+
this.children = children // DAG structure
|
|
80
|
+
this.timestamp = new Date().toISOString()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toDAG() {
|
|
84
|
+
return {
|
|
85
|
+
id: this.id,
|
|
86
|
+
type: this.type,
|
|
87
|
+
value: this.value,
|
|
88
|
+
justification: this.justification,
|
|
89
|
+
children: this.children.map(c => c.toDAG()),
|
|
90
|
+
timestamp: this.timestamp
|
|
125
91
|
}
|
|
126
|
-
},
|
|
127
|
-
'kg.embeddings.search': {
|
|
128
|
-
name: 'kg.embeddings.search',
|
|
129
|
-
input: TypeId.String,
|
|
130
|
-
output: TypeId.List('SimilarEntity'),
|
|
131
|
-
description: 'Find semantically similar entities via HNSW index',
|
|
132
|
-
domain: 'embeddings',
|
|
133
|
-
constraints: { maxK: 100, minThreshold: 0.0, maxThreshold: 1.0 }
|
|
134
|
-
},
|
|
135
|
-
'kg.graphframe.pagerank': {
|
|
136
|
-
name: 'kg.graphframe.pagerank',
|
|
137
|
-
input: TypeId.Map('String', 'Float64'),
|
|
138
|
-
output: TypeId.Map('String', 'Float64'),
|
|
139
|
-
description: 'Compute PageRank to find central entities',
|
|
140
|
-
domain: 'graph_analytics'
|
|
141
|
-
},
|
|
142
|
-
'kg.graphframe.triangles': {
|
|
143
|
-
name: 'kg.graphframe.triangles',
|
|
144
|
-
input: TypeId.String,
|
|
145
|
-
output: TypeId.Int64,
|
|
146
|
-
description: 'Count triangles in graph (fraud ring indicator)',
|
|
147
|
-
domain: 'graph_analytics'
|
|
148
|
-
},
|
|
149
|
-
'kg.graphframe.components': {
|
|
150
|
-
name: 'kg.graphframe.components',
|
|
151
|
-
input: TypeId.String,
|
|
152
|
-
output: TypeId.Map('String', 'Int64'),
|
|
153
|
-
description: 'Find connected components (network clusters)',
|
|
154
|
-
domain: 'graph_analytics'
|
|
155
92
|
}
|
|
156
93
|
}
|
|
157
94
|
|
|
158
95
|
// ============================================================================
|
|
159
|
-
//
|
|
96
|
+
// EXECUTION TRACE (Full Explainability)
|
|
160
97
|
// ============================================================================
|
|
161
98
|
|
|
162
99
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
100
|
+
* ExecutionTrace - Complete record of agent reasoning
|
|
101
|
+
* Shows: what SPARQL was executed, what rules were applied, what DAG was built
|
|
165
102
|
*/
|
|
166
|
-
class
|
|
167
|
-
constructor(
|
|
168
|
-
this.
|
|
169
|
-
this.
|
|
170
|
-
this.
|
|
103
|
+
class ExecutionTrace {
|
|
104
|
+
constructor() {
|
|
105
|
+
this.steps = []
|
|
106
|
+
this.sparqlQueries = []
|
|
107
|
+
this.rulesApplied = []
|
|
108
|
+
this.proofRoots = []
|
|
109
|
+
this.startTime = Date.now()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
addStep(step) {
|
|
113
|
+
this.steps.push({
|
|
114
|
+
...step,
|
|
115
|
+
timestamp: Date.now() - this.startTime
|
|
116
|
+
})
|
|
171
117
|
}
|
|
172
118
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
async plan(prompt, context = {}) {
|
|
180
|
-
// Step 1: Decompose natural language into intent
|
|
181
|
-
const intent = this.decomposeIntent(prompt)
|
|
182
|
-
|
|
183
|
-
// Step 2: Select tools based on intent
|
|
184
|
-
const selectedTools = this.selectTools(intent)
|
|
185
|
-
|
|
186
|
-
// Step 3: Validate type composition
|
|
187
|
-
const validation = this.validateComposition(selectedTools)
|
|
188
|
-
if (!validation.valid) {
|
|
189
|
-
throw new Error(`Type composition error: ${validation.error}`)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Step 4: Generate execution plan
|
|
193
|
-
const plan = {
|
|
194
|
-
id: `plan_${Date.now()}`,
|
|
195
|
-
prompt,
|
|
196
|
-
intent,
|
|
197
|
-
steps: selectedTools.map((tool, i) => ({
|
|
198
|
-
id: i + 1,
|
|
199
|
-
tool: tool.name,
|
|
200
|
-
input_type: tool.input,
|
|
201
|
-
output_type: tool.output,
|
|
202
|
-
args: this.generateArgs(tool, intent, context)
|
|
203
|
-
})),
|
|
204
|
-
type_chain: validation.chain,
|
|
205
|
-
confidence: this.calculateConfidence(intent, selectedTools),
|
|
206
|
-
explanation: `Execute ${intent.action} using ${selectedTools.length} tools`
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
this.planHistory.push(plan)
|
|
210
|
-
return plan
|
|
119
|
+
addSparql(query, results) {
|
|
120
|
+
this.sparqlQueries.push({
|
|
121
|
+
query,
|
|
122
|
+
resultCount: results.length,
|
|
123
|
+
timestamp: Date.now() - this.startTime
|
|
124
|
+
})
|
|
211
125
|
}
|
|
212
126
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
127
|
+
addRule(rule, premises, conclusion) {
|
|
128
|
+
this.rulesApplied.push({
|
|
129
|
+
rule,
|
|
130
|
+
premises,
|
|
131
|
+
conclusion,
|
|
132
|
+
timestamp: Date.now() - this.startTime
|
|
133
|
+
})
|
|
134
|
+
}
|
|
218
135
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
target: 'claims',
|
|
223
|
-
domain: 'insurance_fraud',
|
|
224
|
-
entities: ['claims', 'claimants', 'providers']
|
|
225
|
-
}
|
|
226
|
-
}
|
|
136
|
+
addProof(proofNode) {
|
|
137
|
+
this.proofRoots.push(proofNode)
|
|
138
|
+
}
|
|
227
139
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
140
|
+
toExplainableOutput() {
|
|
141
|
+
return {
|
|
142
|
+
execution_time_ms: Date.now() - this.startTime,
|
|
143
|
+
steps: this.steps,
|
|
144
|
+
sparql_queries: this.sparqlQueries,
|
|
145
|
+
rules_applied: this.rulesApplied,
|
|
146
|
+
proof_dag: this.proofRoots.map(p => p.toDAG()),
|
|
147
|
+
summary: {
|
|
148
|
+
total_sparql_queries: this.sparqlQueries.length,
|
|
149
|
+
total_rules_applied: this.rulesApplied.length,
|
|
150
|
+
proof_depth: this._maxDepth(this.proofRoots)
|
|
233
151
|
}
|
|
234
152
|
}
|
|
153
|
+
}
|
|
235
154
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
}
|
|
155
|
+
_maxDepth(nodes) {
|
|
156
|
+
if (!nodes.length) return 0
|
|
157
|
+
return 1 + Math.max(0, ...nodes.map(n => this._maxDepth(n.children)))
|
|
158
|
+
}
|
|
159
|
+
}
|
|
243
160
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
target: 'academic',
|
|
248
|
-
domain: 'lubm'
|
|
249
|
-
}
|
|
250
|
-
}
|
|
161
|
+
// ============================================================================
|
|
162
|
+
// DATALOG RULES (Configurable Reasoning)
|
|
163
|
+
// ============================================================================
|
|
251
164
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
165
|
+
/**
|
|
166
|
+
* DatalogRuleSet - Configurable rules for agent reasoning
|
|
167
|
+
* Users can add/remove/customize rules
|
|
168
|
+
*/
|
|
169
|
+
class DatalogRuleSet {
|
|
170
|
+
constructor() {
|
|
171
|
+
this.rules = new Map()
|
|
172
|
+
this._loadDefaultRules()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
_loadDefaultRules() {
|
|
176
|
+
// Fraud detection rules
|
|
177
|
+
this.addRule('potential_fraud', {
|
|
178
|
+
head: { predicate: 'potential_fraud', args: ['?claim'] },
|
|
179
|
+
body: [
|
|
180
|
+
{ predicate: 'claim', args: ['?claim', '?amount', '?claimant'] },
|
|
181
|
+
{ predicate: 'risk_score', args: ['?claimant', '?score'] },
|
|
182
|
+
{ filter: '?score > 0.7' }
|
|
183
|
+
],
|
|
184
|
+
description: 'Claims from high-risk claimants'
|
|
185
|
+
})
|
|
258
186
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
this.tools['kg.graphframe.triangles'],
|
|
267
|
-
this.tools['kg.datalog.infer']
|
|
187
|
+
this.addRule('collusion_pattern', {
|
|
188
|
+
head: { predicate: 'collusion', args: ['?a', '?b', '?provider'] },
|
|
189
|
+
body: [
|
|
190
|
+
{ predicate: 'claims_with', args: ['?a', '?provider'] },
|
|
191
|
+
{ predicate: 'claims_with', args: ['?b', '?provider'] },
|
|
192
|
+
{ predicate: 'knows', args: ['?a', '?b'] },
|
|
193
|
+
{ filter: '?a != ?b' }
|
|
268
194
|
],
|
|
269
|
-
'
|
|
270
|
-
|
|
195
|
+
description: 'Two claimants who know each other using same provider'
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
this.addRule('circular_payment', {
|
|
199
|
+
head: { predicate: 'circular_payment', args: ['?a', '?b', '?c'] },
|
|
200
|
+
body: [
|
|
201
|
+
{ predicate: 'paid', args: ['?a', '?b'] },
|
|
202
|
+
{ predicate: 'paid', args: ['?b', '?c'] },
|
|
203
|
+
{ predicate: 'paid', args: ['?c', '?a'] }
|
|
271
204
|
],
|
|
272
|
-
'
|
|
273
|
-
|
|
205
|
+
description: 'Circular payment pattern (A->B->C->A)'
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// Academic rules (LUBM)
|
|
209
|
+
this.addRule('advisor_relationship', {
|
|
210
|
+
head: { predicate: 'advised_by', args: ['?student', '?professor'] },
|
|
211
|
+
body: [
|
|
212
|
+
{ predicate: 'type', args: ['?student', 'Student'] },
|
|
213
|
+
{ predicate: 'advisor', args: ['?student', '?professor'] }
|
|
274
214
|
],
|
|
275
|
-
'
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
215
|
+
description: 'Student-advisor relationship'
|
|
216
|
+
})
|
|
217
|
+
}
|
|
279
218
|
|
|
280
|
-
|
|
219
|
+
addRule(name, rule) {
|
|
220
|
+
this.rules.set(name, {
|
|
221
|
+
name,
|
|
222
|
+
...rule,
|
|
223
|
+
added_at: new Date().toISOString()
|
|
224
|
+
})
|
|
281
225
|
}
|
|
282
226
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
validateComposition(tools) {
|
|
287
|
-
for (let i = 0; i < tools.length - 1; i++) {
|
|
288
|
-
const current = tools[i]
|
|
289
|
-
const next = tools[i + 1]
|
|
290
|
-
if (!TypeId.isCompatible(current.output, next.input)) {
|
|
291
|
-
return {
|
|
292
|
-
valid: false,
|
|
293
|
-
error: `${current.name}.output (${current.output}) is incompatible with ${next.name}.input (${next.input})`
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
227
|
+
removeRule(name) {
|
|
228
|
+
return this.rules.delete(name)
|
|
229
|
+
}
|
|
297
230
|
|
|
298
|
-
|
|
299
|
-
return
|
|
231
|
+
getRule(name) {
|
|
232
|
+
return this.rules.get(name)
|
|
300
233
|
}
|
|
301
234
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
*/
|
|
305
|
-
generateArgs(tool, intent, context) {
|
|
306
|
-
switch (tool.name) {
|
|
307
|
-
case 'kg.sparql.query':
|
|
308
|
-
return { query: this._generateSPARQL(intent) }
|
|
309
|
-
case 'kg.graphframe.triangles':
|
|
310
|
-
return { graph: 'default' }
|
|
311
|
-
case 'kg.datalog.infer':
|
|
312
|
-
return { rules: ['potential_collusion', 'staged_accident'] }
|
|
313
|
-
case 'kg.embeddings.search':
|
|
314
|
-
return { entityId: intent.entities?.[0] || 'unknown', k: 10, threshold: 0.7 }
|
|
315
|
-
default:
|
|
316
|
-
return {}
|
|
317
|
-
}
|
|
235
|
+
getAllRules() {
|
|
236
|
+
return Array.from(this.rules.values())
|
|
318
237
|
}
|
|
319
238
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return `PREFIX ins: <http://insurance.example.org/>
|
|
323
|
-
SELECT ?claim ?claimant ?amount ?riskScore
|
|
324
|
-
WHERE {
|
|
325
|
-
?claim a ins:Claim ;
|
|
326
|
-
ins:claimant ?claimant ;
|
|
327
|
-
ins:amount ?amount ;
|
|
328
|
-
ins:riskScore ?riskScore .
|
|
329
|
-
FILTER (?riskScore > 0.7)
|
|
330
|
-
}`
|
|
331
|
-
}
|
|
332
|
-
return 'SELECT * WHERE { ?s ?p ?o } LIMIT 10'
|
|
239
|
+
toSparqlConstructs() {
|
|
240
|
+
return this.getAllRules().map(rule => this._ruleToSparql(rule))
|
|
333
241
|
}
|
|
334
242
|
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
243
|
+
_ruleToSparql(rule) {
|
|
244
|
+
const headTriple = `?${rule.head.args[0]} <http://hypermind.ai/rules#${rule.head.predicate}> ?result`
|
|
245
|
+
const bodyPatterns = rule.body
|
|
246
|
+
.filter(b => b.predicate)
|
|
247
|
+
.map(b => `?${b.args[0]} <http://hypermind.ai/kg#${b.predicate}> ?${b.args[1]}`)
|
|
248
|
+
.join(' . ')
|
|
249
|
+
const filters = rule.body
|
|
250
|
+
.filter(b => b.filter)
|
|
251
|
+
.map(b => `FILTER(${b.filter})`)
|
|
252
|
+
.join(' ')
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
name: rule.name,
|
|
256
|
+
sparql: `CONSTRUCT { ${headTriple} } WHERE { ${bodyPatterns} ${filters} }`
|
|
257
|
+
}
|
|
340
258
|
}
|
|
341
259
|
}
|
|
342
260
|
|
|
343
261
|
// ============================================================================
|
|
344
|
-
// WASM SANDBOX (
|
|
262
|
+
// WASM SANDBOX (Secure Execution)
|
|
345
263
|
// ============================================================================
|
|
346
264
|
|
|
347
265
|
/**
|
|
348
|
-
* WasmSandbox -
|
|
349
|
-
*
|
|
350
|
-
* All interaction with the Rust core flows through WASM for complete security:
|
|
351
|
-
* - Isolated linear memory (no direct host access)
|
|
352
|
-
* - CPU fuel metering (configurable operation limits)
|
|
353
|
-
* - Capability-based permissions (ReadKG, WriteKG, ExecuteTool)
|
|
354
|
-
* - Memory limits (configurable maximum allocation)
|
|
355
|
-
* - Full audit logging (all tool invocations recorded)
|
|
356
|
-
*
|
|
357
|
-
* The WASM sandbox ensures that agent tool execution cannot:
|
|
358
|
-
* - Access the filesystem
|
|
359
|
-
* - Make unauthorized network calls
|
|
360
|
-
* - Exceed allocated resources
|
|
361
|
-
* - Bypass security boundaries
|
|
362
|
-
*
|
|
363
|
-
* @example
|
|
364
|
-
* const sandbox = new WasmSandbox({
|
|
365
|
-
* capabilities: ['ReadKG', 'ExecuteTool'],
|
|
366
|
-
* fuelLimit: 1000000,
|
|
367
|
-
* maxMemory: 64 * 1024 * 1024
|
|
368
|
-
* })
|
|
266
|
+
* WasmSandbox - Capability-based security with fuel metering
|
|
369
267
|
*/
|
|
370
268
|
class WasmSandbox {
|
|
371
269
|
constructor(config = {}) {
|
|
372
|
-
this.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
capabilities: config.capabilities || ['ReadKG', 'ExecuteTool'],
|
|
376
|
-
fuelLimit: config.fuelLimit || 1000000 // 1M fuel units
|
|
377
|
-
}
|
|
378
|
-
this.fuel = this.config.fuelLimit
|
|
379
|
-
this.memoryUsed = 0
|
|
270
|
+
this.capabilities = new Set(config.capabilities || ['ReadKG', 'ExecuteTool'])
|
|
271
|
+
this.fuelLimit = config.fuelLimit || 1000000
|
|
272
|
+
this.fuel = this.fuelLimit
|
|
380
273
|
this.auditLog = []
|
|
381
|
-
this.startTime = null
|
|
382
274
|
}
|
|
383
275
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
*/
|
|
387
|
-
createObjectProxy(tools) {
|
|
388
|
-
const sandbox = this
|
|
389
|
-
|
|
390
|
-
return new Proxy({}, {
|
|
391
|
-
get(target, toolName) {
|
|
392
|
-
return async (args) => {
|
|
393
|
-
// Security Check 1: Capability verification
|
|
394
|
-
if (!sandbox.hasCapability('ExecuteTool')) {
|
|
395
|
-
const error = `BLOCKED: Missing ExecuteTool capability`
|
|
396
|
-
sandbox._logAudit(toolName, args, null, 'DENIED', error)
|
|
397
|
-
throw new Error(error)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Security Check 2: Fuel metering
|
|
401
|
-
const fuelCost = sandbox._calculateFuelCost(toolName, args)
|
|
402
|
-
if (sandbox.fuel < fuelCost) {
|
|
403
|
-
const error = `BLOCKED: Insufficient fuel (need ${fuelCost}, have ${sandbox.fuel})`
|
|
404
|
-
sandbox._logAudit(toolName, args, null, 'DENIED', error)
|
|
405
|
-
throw new Error(error)
|
|
406
|
-
}
|
|
407
|
-
sandbox.fuel -= fuelCost
|
|
408
|
-
|
|
409
|
-
// Security Check 3: Memory bounds
|
|
410
|
-
const estimatedMemory = JSON.stringify(args).length * 2
|
|
411
|
-
if (sandbox.memoryUsed + estimatedMemory > sandbox.config.maxMemory) {
|
|
412
|
-
const error = `BLOCKED: Memory limit exceeded`
|
|
413
|
-
sandbox._logAudit(toolName, args, null, 'DENIED', error)
|
|
414
|
-
throw new Error(error)
|
|
415
|
-
}
|
|
416
|
-
sandbox.memoryUsed += estimatedMemory
|
|
417
|
-
|
|
418
|
-
// Security Check 4: Execution time
|
|
419
|
-
if (!sandbox.startTime) {
|
|
420
|
-
sandbox.startTime = Date.now()
|
|
421
|
-
}
|
|
422
|
-
if (Date.now() - sandbox.startTime > sandbox.config.maxExecTime) {
|
|
423
|
-
const error = `BLOCKED: Execution time limit exceeded`
|
|
424
|
-
sandbox._logAudit(toolName, args, null, 'DENIED', error)
|
|
425
|
-
throw new Error(error)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Execute tool
|
|
429
|
-
const tool = tools[toolName]
|
|
430
|
-
if (!tool) {
|
|
431
|
-
const error = `BLOCKED: Unknown tool ${toolName}`
|
|
432
|
-
sandbox._logAudit(toolName, args, null, 'DENIED', error)
|
|
433
|
-
throw new Error(error)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const result = tool.execute ? await tool.execute(args) : { status: 'success' }
|
|
437
|
-
sandbox._logAudit(toolName, args, result, 'OK')
|
|
438
|
-
|
|
439
|
-
return result
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
})
|
|
276
|
+
hasCapability(cap) {
|
|
277
|
+
return this.capabilities.has(cap)
|
|
443
278
|
}
|
|
444
279
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
_calculateFuelCost(toolName, args) {
|
|
450
|
-
const baseCosts = {
|
|
451
|
-
'kg.sparql.query': 500,
|
|
452
|
-
'kg.motif.find': 1000,
|
|
453
|
-
'kg.datalog.infer': 2000,
|
|
454
|
-
'kg.embeddings.search': 300,
|
|
455
|
-
'kg.graphframe.pagerank': 5000,
|
|
456
|
-
'kg.graphframe.triangles': 1500,
|
|
457
|
-
'kg.graphframe.components': 2000
|
|
280
|
+
consumeFuel(amount) {
|
|
281
|
+
if (this.fuel < amount) {
|
|
282
|
+
throw new Error(`Insufficient fuel: need ${amount}, have ${this.fuel}`)
|
|
458
283
|
}
|
|
459
|
-
|
|
284
|
+
this.fuel -= amount
|
|
460
285
|
}
|
|
461
286
|
|
|
462
|
-
|
|
287
|
+
log(action, args, result, status) {
|
|
463
288
|
this.auditLog.push({
|
|
464
289
|
timestamp: new Date().toISOString(),
|
|
465
|
-
|
|
466
|
-
args,
|
|
467
|
-
result: result ? { status: result.status } : null,
|
|
290
|
+
action,
|
|
291
|
+
args: JSON.stringify(args).slice(0, 200),
|
|
468
292
|
status,
|
|
469
|
-
error,
|
|
470
293
|
fuel_remaining: this.fuel
|
|
471
294
|
})
|
|
472
295
|
}
|
|
@@ -474,2591 +297,1109 @@ class WasmSandbox {
|
|
|
474
297
|
getAuditLog() {
|
|
475
298
|
return this.auditLog
|
|
476
299
|
}
|
|
477
|
-
|
|
478
|
-
getMetrics() {
|
|
479
|
-
return {
|
|
480
|
-
fuel_initial: this.config.fuelLimit,
|
|
481
|
-
fuel_remaining: this.fuel,
|
|
482
|
-
fuel_consumed: this.config.fuelLimit - this.fuel,
|
|
483
|
-
memory_used: this.memoryUsed,
|
|
484
|
-
memory_limit: this.config.maxMemory,
|
|
485
|
-
capabilities: this.config.capabilities,
|
|
486
|
-
tool_calls: this.auditLog.length
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
300
|
}
|
|
490
301
|
|
|
491
302
|
// ============================================================================
|
|
492
|
-
// MEMORY
|
|
303
|
+
// MEMORY MANAGER (Episode Memory with KG Integration)
|
|
493
304
|
// ============================================================================
|
|
494
305
|
|
|
495
306
|
/**
|
|
496
|
-
*
|
|
497
|
-
*/
|
|
498
|
-
const AgentState = {
|
|
499
|
-
CREATED: 'CREATED',
|
|
500
|
-
READY: 'READY',
|
|
501
|
-
RUNNING: 'RUNNING',
|
|
502
|
-
PAUSED: 'PAUSED',
|
|
503
|
-
COMPLETED: 'COMPLETED',
|
|
504
|
-
FAILED: 'FAILED'
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* AgentRuntime - Core agent identity and state management
|
|
509
|
-
*
|
|
510
|
-
* Provides:
|
|
511
|
-
* - Unique agent identity (UUID)
|
|
512
|
-
* - Lifecycle state management
|
|
513
|
-
* - Memory layer orchestration
|
|
514
|
-
* - Execution context tracking
|
|
307
|
+
* MemoryManager - Stores agent episodes in KG for retrieval
|
|
515
308
|
*/
|
|
516
|
-
class
|
|
517
|
-
constructor(
|
|
518
|
-
|
|
519
|
-
this.
|
|
520
|
-
this.
|
|
521
|
-
this.
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
recency: 0.3, // α - time decay
|
|
538
|
-
relevance: 0.5, // β - semantic similarity
|
|
539
|
-
importance: 0.2 // γ - access frequency
|
|
540
|
-
},
|
|
541
|
-
recencyDecayRate: config.recencyDecayRate || 0.995 // per hour
|
|
309
|
+
class MemoryManager {
|
|
310
|
+
constructor(kg, embeddingService) {
|
|
311
|
+
this.kg = kg
|
|
312
|
+
this.embeddingService = embeddingService
|
|
313
|
+
this.episodes = []
|
|
314
|
+
this.decayRate = 0.995 // Per hour
|
|
315
|
+
this.weights = { recency: 0.3, relevance: 0.5, importance: 0.2 }
|
|
316
|
+
this.workingMemory = new Map()
|
|
317
|
+
this.executions = []
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async store(prompt, result, success, durationMs) {
|
|
321
|
+
const episode = {
|
|
322
|
+
id: crypto.randomUUID(),
|
|
323
|
+
prompt,
|
|
324
|
+
result,
|
|
325
|
+
success,
|
|
326
|
+
durationMs,
|
|
327
|
+
timestamp: new Date().toISOString(),
|
|
328
|
+
accessCount: 0,
|
|
329
|
+
embedding: null
|
|
542
330
|
}
|
|
543
331
|
|
|
544
|
-
//
|
|
545
|
-
this.
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
totalExecutions: 0,
|
|
552
|
-
successfulExecutions: 0,
|
|
553
|
-
failedExecutions: 0,
|
|
554
|
-
totalMemoryRetrievals: 0,
|
|
555
|
-
avgExecutionTimeMs: 0
|
|
332
|
+
// Generate embedding for semantic retrieval
|
|
333
|
+
if (this.embeddingService) {
|
|
334
|
+
try {
|
|
335
|
+
episode.embedding = await this._getEmbedding(prompt)
|
|
336
|
+
} catch (err) {
|
|
337
|
+
// Continue without embedding
|
|
338
|
+
}
|
|
556
339
|
}
|
|
557
|
-
}
|
|
558
340
|
|
|
559
|
-
|
|
560
|
-
* Transition agent to a new state
|
|
561
|
-
*/
|
|
562
|
-
transitionTo(newState, reason = '') {
|
|
563
|
-
const validTransitions = {
|
|
564
|
-
[AgentState.CREATED]: [AgentState.READY],
|
|
565
|
-
[AgentState.READY]: [AgentState.RUNNING, AgentState.PAUSED],
|
|
566
|
-
[AgentState.RUNNING]: [AgentState.READY, AgentState.COMPLETED, AgentState.FAILED, AgentState.PAUSED],
|
|
567
|
-
[AgentState.PAUSED]: [AgentState.READY, AgentState.RUNNING],
|
|
568
|
-
[AgentState.COMPLETED]: [AgentState.READY],
|
|
569
|
-
[AgentState.FAILED]: [AgentState.READY]
|
|
570
|
-
}
|
|
341
|
+
this.episodes.push(episode)
|
|
571
342
|
|
|
572
|
-
|
|
573
|
-
|
|
343
|
+
// Also store in KG as RDF
|
|
344
|
+
if (this.kg) {
|
|
345
|
+
try {
|
|
346
|
+
const ttl = this._episodeToTurtle(episode)
|
|
347
|
+
this.kg.loadTtl(ttl, 'http://hypermind.ai/memory/')
|
|
348
|
+
} catch (err) {
|
|
349
|
+
// Continue without KG storage
|
|
350
|
+
}
|
|
574
351
|
}
|
|
575
352
|
|
|
576
|
-
|
|
577
|
-
this.stateHistory.push({
|
|
578
|
-
state: newState,
|
|
579
|
-
timestamp: new Date().toISOString(),
|
|
580
|
-
reason
|
|
581
|
-
})
|
|
582
|
-
this.lastActiveAt = new Date().toISOString()
|
|
583
|
-
|
|
584
|
-
return this
|
|
353
|
+
return episode
|
|
585
354
|
}
|
|
586
355
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
*/
|
|
590
|
-
ready() {
|
|
591
|
-
return this.transitionTo(AgentState.READY, 'Agent initialized and ready')
|
|
592
|
-
}
|
|
356
|
+
async retrieve(query, limit = 10) {
|
|
357
|
+
if (!this.episodes.length) return []
|
|
593
358
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
this.
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
359
|
+
const queryEmbedding = this.embeddingService
|
|
360
|
+
? await this._getEmbedding(query)
|
|
361
|
+
: null
|
|
362
|
+
|
|
363
|
+
const scored = this.episodes.map(ep => ({
|
|
364
|
+
episode: ep,
|
|
365
|
+
score: this._calculateScore(ep, query, queryEmbedding)
|
|
366
|
+
}))
|
|
367
|
+
|
|
368
|
+
return scored
|
|
369
|
+
.sort((a, b) => b.score - a.score)
|
|
370
|
+
.slice(0, limit)
|
|
371
|
+
.map(s => {
|
|
372
|
+
s.episode.accessCount++
|
|
373
|
+
return s
|
|
374
|
+
})
|
|
607
375
|
}
|
|
608
376
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
this.currentExecution.durationMs = this.currentExecution.endTime - this.currentExecution.startTime
|
|
617
|
-
this.currentExecution.result = result
|
|
618
|
-
this.currentExecution.success = success
|
|
619
|
-
|
|
620
|
-
// Update metrics
|
|
621
|
-
this.metrics.totalExecutions++
|
|
622
|
-
if (success) {
|
|
623
|
-
this.metrics.successfulExecutions++
|
|
377
|
+
_calculateScore(episode, query, queryEmbedding) {
|
|
378
|
+
const hoursAgo = (Date.now() - new Date(episode.timestamp).getTime()) / 3600000
|
|
379
|
+
const recency = Math.pow(this.decayRate, hoursAgo)
|
|
380
|
+
|
|
381
|
+
let relevance = 0
|
|
382
|
+
if (queryEmbedding && episode.embedding) {
|
|
383
|
+
relevance = this._cosineSimilarity(queryEmbedding, episode.embedding)
|
|
624
384
|
} else {
|
|
625
|
-
|
|
385
|
+
// Fallback to word overlap
|
|
386
|
+
relevance = this._wordOverlap(query, episode.prompt)
|
|
626
387
|
}
|
|
627
|
-
this.metrics.avgExecutionTimeMs =
|
|
628
|
-
(this.metrics.avgExecutionTimeMs * (this.metrics.totalExecutions - 1) +
|
|
629
|
-
this.currentExecution.durationMs) / this.metrics.totalExecutions
|
|
630
388
|
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
this.transitionTo(AgentState.READY, `Execution completed: ${success ? 'success' : 'failed'}`)
|
|
389
|
+
const maxAccess = Math.max(...this.episodes.map(e => e.accessCount), 1)
|
|
390
|
+
const importance = Math.log10(episode.accessCount + 1) / Math.log10(maxAccess + 1)
|
|
634
391
|
|
|
635
|
-
return
|
|
392
|
+
return this.weights.recency * recency +
|
|
393
|
+
this.weights.relevance * relevance +
|
|
394
|
+
this.weights.importance * importance
|
|
636
395
|
}
|
|
637
396
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
version: this.version,
|
|
646
|
-
createdAt: this.createdAt,
|
|
647
|
-
state: this.state,
|
|
648
|
-
executionCount: this.executionCount,
|
|
649
|
-
lastActiveAt: this.lastActiveAt
|
|
397
|
+
_cosineSimilarity(a, b) {
|
|
398
|
+
if (!a || !b || a.length !== b.length) return 0
|
|
399
|
+
let dot = 0, normA = 0, normB = 0
|
|
400
|
+
for (let i = 0; i < a.length; i++) {
|
|
401
|
+
dot += a[i] * b[i]
|
|
402
|
+
normA += a[i] * a[i]
|
|
403
|
+
normB += b[i] * b[i]
|
|
650
404
|
}
|
|
405
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB) + 1e-9)
|
|
651
406
|
}
|
|
652
407
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
return
|
|
658
|
-
...this.metrics,
|
|
659
|
-
uptime: Date.now() - new Date(this.createdAt).getTime(),
|
|
660
|
-
currentState: this.state
|
|
661
|
-
}
|
|
408
|
+
_wordOverlap(a, b) {
|
|
409
|
+
const wordsA = new Set(a.toLowerCase().split(/\s+/))
|
|
410
|
+
const wordsB = new Set(b.toLowerCase().split(/\s+/))
|
|
411
|
+
const intersection = [...wordsA].filter(w => wordsB.has(w))
|
|
412
|
+
return intersection.length / Math.max(wordsA.size, wordsB.size, 1)
|
|
662
413
|
}
|
|
663
|
-
}
|
|
664
414
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
* Fast, ephemeral memory for the current task:
|
|
669
|
-
* - Current conversation context
|
|
670
|
-
* - Active bindings and variables
|
|
671
|
-
* - Tool execution results (recent)
|
|
672
|
-
* - Scratchpad for reasoning
|
|
673
|
-
*/
|
|
674
|
-
class WorkingMemory {
|
|
675
|
-
constructor(capacity = 100) {
|
|
676
|
-
this.capacity = capacity
|
|
677
|
-
this.context = [] // Current conversation/task context
|
|
678
|
-
this.bindings = new Map() // Variable bindings
|
|
679
|
-
this.scratchpad = new Map() // Temporary reasoning space
|
|
680
|
-
this.toolResults = [] // Recent tool execution results
|
|
681
|
-
this.focus = null // Current focus entity/topic
|
|
415
|
+
async _getEmbedding(text) {
|
|
416
|
+
if (!this.embeddingService) return null
|
|
417
|
+
return this.embeddingService.embed(text)
|
|
682
418
|
}
|
|
683
419
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
this.context.push({
|
|
689
|
-
...item,
|
|
690
|
-
timestamp: item.timestamp || Date.now(),
|
|
691
|
-
id: item.id !== undefined ? item.id : `ctx_${Date.now()}`
|
|
692
|
-
})
|
|
693
|
-
|
|
694
|
-
// Evict oldest if over capacity
|
|
695
|
-
while (this.context.length > this.capacity) {
|
|
696
|
-
this.context.shift()
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
return this
|
|
700
|
-
}
|
|
420
|
+
_episodeToTurtle(episode) {
|
|
421
|
+
return `
|
|
422
|
+
@prefix am: <http://hypermind.ai/memory#> .
|
|
423
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
701
424
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
timestamp: Date.now(),
|
|
709
|
-
accessCount: 0
|
|
710
|
-
})
|
|
711
|
-
return this
|
|
425
|
+
<http://hypermind.ai/episode/${episode.id}> a am:Episode ;
|
|
426
|
+
am:prompt "${episode.prompt.replace(/"/g, '\\"')}" ;
|
|
427
|
+
am:success "${episode.success}"^^xsd:boolean ;
|
|
428
|
+
am:durationMs "${episode.durationMs}"^^xsd:integer ;
|
|
429
|
+
am:timestamp "${episode.timestamp}"^^xsd:dateTime .
|
|
430
|
+
`
|
|
712
431
|
}
|
|
713
432
|
|
|
714
433
|
/**
|
|
715
|
-
*
|
|
434
|
+
* Store a tool execution as an episode for audit trail
|
|
435
|
+
* @param {Object} execution - Execution record with id, prompt, tool, output, success, durationMs
|
|
716
436
|
*/
|
|
717
|
-
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
437
|
+
async storeExecution(execution) {
|
|
438
|
+
const episode = {
|
|
439
|
+
id: execution.id || crypto.randomUUID(),
|
|
440
|
+
prompt: execution.prompt,
|
|
441
|
+
result: execution.output,
|
|
442
|
+
success: execution.success,
|
|
443
|
+
durationMs: execution.durationMs,
|
|
444
|
+
tool: execution.tool,
|
|
445
|
+
timestamp: new Date().toISOString(),
|
|
446
|
+
accessCount: 0,
|
|
447
|
+
embedding: null
|
|
722
448
|
}
|
|
723
|
-
return null
|
|
724
|
-
}
|
|
725
449
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
result,
|
|
734
|
-
timestamp: Date.now()
|
|
735
|
-
})
|
|
736
|
-
|
|
737
|
-
// Keep only recent results
|
|
738
|
-
if (this.toolResults.length > 20) {
|
|
739
|
-
this.toolResults.shift()
|
|
450
|
+
// Generate embedding for semantic retrieval
|
|
451
|
+
if (this.embeddingService) {
|
|
452
|
+
try {
|
|
453
|
+
episode.embedding = await this._getEmbedding(execution.prompt)
|
|
454
|
+
} catch (err) {
|
|
455
|
+
// Continue without embedding
|
|
456
|
+
}
|
|
740
457
|
}
|
|
741
458
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* Set current focus
|
|
747
|
-
*/
|
|
748
|
-
setFocus(entity) {
|
|
749
|
-
this.focus = {
|
|
750
|
-
entity,
|
|
751
|
-
timestamp: Date.now()
|
|
752
|
-
}
|
|
753
|
-
return this
|
|
754
|
-
}
|
|
459
|
+
this.executions.push(episode)
|
|
460
|
+
this.episodes.push(episode)
|
|
755
461
|
|
|
756
|
-
|
|
757
|
-
* Write to scratchpad
|
|
758
|
-
*/
|
|
759
|
-
scratch(key, value) {
|
|
760
|
-
this.scratchpad.set(key, value)
|
|
761
|
-
return this
|
|
462
|
+
return episode
|
|
762
463
|
}
|
|
763
464
|
|
|
764
465
|
/**
|
|
765
|
-
*
|
|
466
|
+
* Add data to working memory (ephemeral, in-context)
|
|
467
|
+
* @param {Object} data - Data to store in working memory
|
|
766
468
|
*/
|
|
767
|
-
|
|
768
|
-
|
|
469
|
+
addToWorking(data) {
|
|
470
|
+
const key = data.type || `working-${Date.now()}`
|
|
471
|
+
this.workingMemory.set(key, {
|
|
472
|
+
...data,
|
|
473
|
+
addedAt: Date.now()
|
|
474
|
+
})
|
|
475
|
+
return key
|
|
769
476
|
}
|
|
770
477
|
|
|
771
478
|
/**
|
|
772
|
-
* Get
|
|
479
|
+
* Get data from working memory
|
|
480
|
+
* @param {string} key - Key to retrieve
|
|
773
481
|
*/
|
|
774
|
-
|
|
775
|
-
return this.
|
|
482
|
+
getFromWorking(key) {
|
|
483
|
+
return this.workingMemory.get(key)
|
|
776
484
|
}
|
|
777
485
|
|
|
778
486
|
/**
|
|
779
487
|
* Clear working memory
|
|
780
488
|
*/
|
|
781
|
-
|
|
782
|
-
this.
|
|
783
|
-
this.bindings.clear()
|
|
784
|
-
this.scratchpad.clear()
|
|
785
|
-
this.toolResults = []
|
|
786
|
-
this.focus = null
|
|
787
|
-
return this
|
|
489
|
+
clearWorking() {
|
|
490
|
+
this.workingMemory.clear()
|
|
788
491
|
}
|
|
789
492
|
|
|
790
493
|
/**
|
|
791
|
-
*
|
|
494
|
+
* Get memory statistics
|
|
792
495
|
*/
|
|
793
|
-
|
|
496
|
+
getStats() {
|
|
794
497
|
return {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
498
|
+
episodeCount: this.episodes.length,
|
|
499
|
+
executionCount: this.executions.length,
|
|
500
|
+
workingMemorySize: this.workingMemory.size,
|
|
501
|
+
weights: { ...this.weights },
|
|
502
|
+
decayRate: this.decayRate,
|
|
503
|
+
// Structured format for compatibility
|
|
504
|
+
working: {
|
|
505
|
+
contextSize: this.workingMemory.size,
|
|
506
|
+
items: Array.from(this.workingMemory.keys())
|
|
507
|
+
},
|
|
508
|
+
episodic: {
|
|
509
|
+
episodeCount: this.episodes.length,
|
|
510
|
+
executionCount: this.executions.length
|
|
511
|
+
}
|
|
800
512
|
}
|
|
801
513
|
}
|
|
802
514
|
}
|
|
803
515
|
|
|
516
|
+
// ============================================================================
|
|
517
|
+
// HYPERMIND AGENT (Main API - Symbolica Agentica Pattern)
|
|
518
|
+
// ============================================================================
|
|
519
|
+
|
|
804
520
|
/**
|
|
805
|
-
*
|
|
521
|
+
* HyperMindAgent - Neuro-Symbolic AI Agent with ZERO hallucination
|
|
806
522
|
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
523
|
+
* All constructor parameters are required - no backward compatibility needed.
|
|
524
|
+
* The agent spawns a runtime, creates WASM sandbox, and executes via typed tools.
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* const kg = new GraphDB('http://example.org/')
|
|
528
|
+
* const memory = new MemoryManager(kg)
|
|
529
|
+
* const embeddings = new EmbeddingService({ provider: 'mock' })
|
|
530
|
+
*
|
|
531
|
+
* const agent = new HyperMindAgent({
|
|
532
|
+
* kg,
|
|
533
|
+
* memory,
|
|
534
|
+
* embeddings,
|
|
535
|
+
* apiKey: 'sk-...',
|
|
536
|
+
* rules: new DatalogRuleSet()
|
|
537
|
+
* })
|
|
538
|
+
*
|
|
539
|
+
* const result = await agent.call('Find fraudulent claims')
|
|
540
|
+
* console.log(result.answer)
|
|
541
|
+
* console.log(result.explanation.sparql_queries)
|
|
542
|
+
* console.log(result.explanation.rules_applied)
|
|
543
|
+
* console.log(result.explanation.proof_dag)
|
|
812
544
|
*/
|
|
813
|
-
class
|
|
814
|
-
constructor(
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
545
|
+
class HyperMindAgent {
|
|
546
|
+
constructor(config) {
|
|
547
|
+
if (!config.kg) {
|
|
548
|
+
throw new Error('kg (Knowledge Graph) is required')
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
this.kg = config.kg
|
|
552
|
+
this.memory = config.memory || new MemoryManager(config.kg, config.embeddings)
|
|
553
|
+
this.embeddings = config.embeddings || null
|
|
554
|
+
this.apiKey = config.apiKey || null
|
|
555
|
+
this.rules = config.rules || new DatalogRuleSet()
|
|
556
|
+
this.sandbox = new WasmSandbox(config.sandbox || {})
|
|
557
|
+
this.name = config.name || 'hypermind-agent'
|
|
558
|
+
|
|
559
|
+
// Intent patterns for natural language -> tool mapping
|
|
560
|
+
this.intentPatterns = this._buildIntentPatterns()
|
|
818
561
|
}
|
|
819
562
|
|
|
820
563
|
/**
|
|
821
|
-
*
|
|
564
|
+
* Execute a natural language request
|
|
565
|
+
* Returns answer + full explainable AI output
|
|
822
566
|
*/
|
|
823
|
-
async
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
//
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
ep:agentId "${agentId}" ;
|
|
835
|
-
ep:executionId "${execution.id}" ;
|
|
836
|
-
ep:prompt "${this._escapeForTtl(execution.prompt)}" ;
|
|
837
|
-
ep:timestamp "${timestamp}"^^xsd:dateTime ;
|
|
838
|
-
ep:durationMs "${execution.durationMs || 0}"^^xsd:integer ;
|
|
839
|
-
ep:success "${execution.success}"^^xsd:boolean .
|
|
840
|
-
`
|
|
841
|
-
|
|
842
|
-
// Store in GraphDB
|
|
843
|
-
if (this.graphDb && typeof this.graphDb.loadTtl === 'function') {
|
|
844
|
-
await this.graphDb.loadTtl(triples, `${this.namespace}episodes`)
|
|
567
|
+
async call(prompt) {
|
|
568
|
+
const trace = new ExecutionTrace()
|
|
569
|
+
trace.addStep({ type: 'input', prompt })
|
|
570
|
+
|
|
571
|
+
// 1. Check memory for similar past queries
|
|
572
|
+
const memories = await this.memory.retrieve(prompt, 3)
|
|
573
|
+
if (memories.length > 0) {
|
|
574
|
+
trace.addStep({
|
|
575
|
+
type: 'memory_recall',
|
|
576
|
+
similar_queries: memories.map(m => m.episode.prompt)
|
|
577
|
+
})
|
|
845
578
|
}
|
|
846
579
|
|
|
847
|
-
|
|
580
|
+
// 2. Classify intent from natural language (no hallucination - pattern matching)
|
|
581
|
+
const intent = this._classifyIntent(prompt)
|
|
582
|
+
trace.addStep({ type: 'intent_classification', intent })
|
|
583
|
+
|
|
584
|
+
// 3. Generate typed execution plan
|
|
585
|
+
const plan = this._generatePlan(intent, prompt)
|
|
586
|
+
trace.addStep({ type: 'execution_plan', plan })
|
|
587
|
+
|
|
588
|
+
// 4. Execute plan in WASM sandbox
|
|
589
|
+
const results = await this._executePlan(plan, trace)
|
|
590
|
+
|
|
591
|
+
// 5. Apply Datalog rules for inference
|
|
592
|
+
const inferences = await this._applyRules(intent, results, trace)
|
|
593
|
+
|
|
594
|
+
// 6. Build proof DAG (explainable AI)
|
|
595
|
+
const proofRoot = this._buildProofDAG(plan, results, inferences, trace)
|
|
596
|
+
trace.addProof(proofRoot)
|
|
597
|
+
|
|
598
|
+
// 7. Format answer
|
|
599
|
+
const answer = this._formatAnswer(results, inferences, intent)
|
|
600
|
+
|
|
601
|
+
// 8. Store episode in memory
|
|
602
|
+
const startTime = trace.startTime
|
|
603
|
+
await this.memory.store(prompt, answer, true, Date.now() - startTime)
|
|
848
604
|
|
|
849
605
|
return {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
606
|
+
answer,
|
|
607
|
+
explanation: trace.toExplainableOutput(),
|
|
608
|
+
raw_results: results,
|
|
609
|
+
inferences,
|
|
610
|
+
proof: proofRoot.toDAG()
|
|
853
611
|
}
|
|
854
612
|
}
|
|
855
613
|
|
|
856
614
|
/**
|
|
857
|
-
*
|
|
615
|
+
* Configure Datalog rules
|
|
858
616
|
*/
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const triples = `
|
|
863
|
-
@prefix ep: <${this.namespace}> .
|
|
864
|
-
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
865
|
-
|
|
866
|
-
<${invocationUri}> a ep:ToolInvocation ;
|
|
867
|
-
ep:episode <${this.namespace}episode/${episodeId}> ;
|
|
868
|
-
ep:tool "${toolName}" ;
|
|
869
|
-
ep:timestamp "${new Date().toISOString()}"^^xsd:dateTime ;
|
|
870
|
-
ep:success "${success}"^^xsd:boolean .
|
|
871
|
-
`
|
|
617
|
+
addRule(name, rule) {
|
|
618
|
+
this.rules.addRule(name, rule)
|
|
619
|
+
}
|
|
872
620
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
621
|
+
removeRule(name) {
|
|
622
|
+
return this.rules.removeRule(name)
|
|
623
|
+
}
|
|
876
624
|
|
|
877
|
-
|
|
625
|
+
getRules() {
|
|
626
|
+
return this.rules.getAllRules()
|
|
878
627
|
}
|
|
879
628
|
|
|
880
629
|
/**
|
|
881
|
-
*
|
|
630
|
+
* Get audit log from sandbox
|
|
882
631
|
*/
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
const since = options.since || null
|
|
886
|
-
|
|
887
|
-
let query = `
|
|
888
|
-
PREFIX ep: <${this.namespace}>
|
|
889
|
-
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
|
890
|
-
|
|
891
|
-
SELECT ?episode ?prompt ?timestamp ?durationMs ?success
|
|
892
|
-
WHERE {
|
|
893
|
-
?episode a ep:Episode ;
|
|
894
|
-
ep:agentId "${agentId}" ;
|
|
895
|
-
ep:prompt ?prompt ;
|
|
896
|
-
ep:timestamp ?timestamp ;
|
|
897
|
-
ep:durationMs ?durationMs ;
|
|
898
|
-
ep:success ?success .
|
|
899
|
-
${since ? `FILTER(?timestamp > "${since}"^^xsd:dateTime)` : ''}
|
|
900
|
-
}
|
|
901
|
-
ORDER BY DESC(?timestamp)
|
|
902
|
-
LIMIT ${limit}
|
|
903
|
-
`
|
|
904
|
-
|
|
905
|
-
if (this.graphDb && typeof this.graphDb.querySelect === 'function') {
|
|
906
|
-
try {
|
|
907
|
-
const results = await this.graphDb.querySelect(query)
|
|
908
|
-
return results.map(r => ({
|
|
909
|
-
episode: r.bindings?.episode || r.episode,
|
|
910
|
-
prompt: r.bindings?.prompt || r.prompt,
|
|
911
|
-
timestamp: r.bindings?.timestamp || r.timestamp,
|
|
912
|
-
durationMs: parseInt(r.bindings?.durationMs || r.durationMs || '0'),
|
|
913
|
-
success: (r.bindings?.success || r.success) === 'true'
|
|
914
|
-
}))
|
|
915
|
-
} catch (e) {
|
|
916
|
-
// GraphDB not available, return empty
|
|
917
|
-
return []
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
return []
|
|
632
|
+
getAuditLog() {
|
|
633
|
+
return this.sandbox.getAuditLog()
|
|
922
634
|
}
|
|
923
635
|
|
|
924
636
|
/**
|
|
925
|
-
* Get
|
|
637
|
+
* Get agent name
|
|
926
638
|
*/
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
// For now, return recent episodes as fallback
|
|
930
|
-
return this.getEpisodes('*', { limit: k })
|
|
639
|
+
getName() {
|
|
640
|
+
return this.name
|
|
931
641
|
}
|
|
932
642
|
|
|
933
643
|
/**
|
|
934
|
-
*
|
|
644
|
+
* Get model name (defaults to 'mock' if no API key)
|
|
935
645
|
*/
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
return Math.pow(decayRate, hoursElapsed)
|
|
646
|
+
getModel() {
|
|
647
|
+
return this.apiKey ? 'configured' : 'mock'
|
|
939
648
|
}
|
|
940
649
|
|
|
941
|
-
|
|
942
|
-
* Escape string for Turtle format
|
|
943
|
-
*/
|
|
944
|
-
_escapeForTtl(str) {
|
|
945
|
-
if (!str) return ''
|
|
946
|
-
return str
|
|
947
|
-
.replace(/\\/g, '\\\\')
|
|
948
|
-
.replace(/"/g, '\\"')
|
|
949
|
-
.replace(/\n/g, '\\n')
|
|
950
|
-
.replace(/\r/g, '\\r')
|
|
951
|
-
.replace(/\t/g, '\\t')
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
/**
|
|
955
|
-
* Get episode count
|
|
956
|
-
*/
|
|
957
|
-
getEpisodeCount() {
|
|
958
|
-
return this.episodeCount
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
/**
|
|
963
|
-
* LongTermMemory - Source-of-truth Knowledge Graph
|
|
964
|
-
*
|
|
965
|
-
* Read-only access to the ingestion graph (source of truth):
|
|
966
|
-
* - Domain ontologies and schemas
|
|
967
|
-
* - Factual knowledge from data ingestion
|
|
968
|
-
* - Business rules and constraints
|
|
969
|
-
* - Clear separation from agent memory
|
|
970
|
-
*/
|
|
971
|
-
class LongTermMemory {
|
|
972
|
-
constructor(graphDb, config = {}) {
|
|
973
|
-
this.graphDb = graphDb
|
|
974
|
-
this.sourceGraphUri = config.sourceGraphUri || 'http://hypermind.ai/source/'
|
|
975
|
-
this.agentGraphUri = config.agentGraphUri || 'http://hypermind.ai/agent/'
|
|
976
|
-
this.readOnly = config.readOnly !== false // Default to read-only
|
|
977
|
-
this.accessLog = []
|
|
978
|
-
}
|
|
650
|
+
// ---- Private Methods ----
|
|
979
651
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
652
|
+
_buildIntentPatterns() {
|
|
653
|
+
return [
|
|
654
|
+
{
|
|
655
|
+
patterns: ['fraud', 'suspicious', 'risk', 'anomaly'],
|
|
656
|
+
intent: 'detect_fraud',
|
|
657
|
+
tools: ['kg.sparql.query', 'kg.datalog.infer', 'kg.graphframe.triangles']
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
patterns: ['similar', 'like', 'related', 'nearest'],
|
|
661
|
+
intent: 'find_similar',
|
|
662
|
+
tools: ['kg.embeddings.search']
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
patterns: ['explain', 'why', 'how', 'proof', 'derivation'],
|
|
666
|
+
intent: 'explain',
|
|
667
|
+
tools: ['kg.datalog.infer']
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
patterns: ['circular', 'cycle', 'ring', 'loop'],
|
|
671
|
+
intent: 'find_patterns',
|
|
672
|
+
tools: ['kg.motif.find', 'kg.graphframe.triangles']
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
patterns: ['professor', 'student', 'university', 'department', 'course'],
|
|
676
|
+
intent: 'academic_query',
|
|
677
|
+
tools: ['kg.sparql.query']
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
patterns: ['count', 'how many', 'total'],
|
|
681
|
+
intent: 'aggregate',
|
|
682
|
+
tools: ['kg.sparql.query']
|
|
995
683
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
return []
|
|
684
|
+
]
|
|
999
685
|
}
|
|
1000
686
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
*/
|
|
1004
|
-
async getEntityFacts(entityUri, limit = 50) {
|
|
1005
|
-
const query = `
|
|
1006
|
-
SELECT ?p ?o WHERE {
|
|
1007
|
-
<${entityUri}> ?p ?o .
|
|
1008
|
-
} LIMIT ${limit}
|
|
1009
|
-
`
|
|
1010
|
-
return this.querySource(query)
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
/**
|
|
1014
|
-
* Get entities by type from source graph
|
|
1015
|
-
*/
|
|
1016
|
-
async getEntitiesByType(typeUri, limit = 100) {
|
|
1017
|
-
const query = `
|
|
1018
|
-
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
1019
|
-
SELECT ?entity WHERE {
|
|
1020
|
-
?entity rdf:type <${typeUri}> .
|
|
1021
|
-
} LIMIT ${limit}
|
|
1022
|
-
`
|
|
1023
|
-
return this.querySource(query)
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
/**
|
|
1027
|
-
* Get related entities (1-hop neighbors)
|
|
1028
|
-
*/
|
|
1029
|
-
async getRelatedEntities(entityUri, direction = 'both') {
|
|
1030
|
-
let query
|
|
1031
|
-
if (direction === 'out') {
|
|
1032
|
-
query = `SELECT ?p ?target WHERE { <${entityUri}> ?p ?target . FILTER(isIRI(?target)) }`
|
|
1033
|
-
} else if (direction === 'in') {
|
|
1034
|
-
query = `SELECT ?source ?p WHERE { ?source ?p <${entityUri}> . FILTER(isIRI(?source)) }`
|
|
1035
|
-
} else {
|
|
1036
|
-
query = `
|
|
1037
|
-
SELECT ?direction ?p ?entity WHERE {
|
|
1038
|
-
{ <${entityUri}> ?p ?entity . BIND("out" AS ?direction) FILTER(isIRI(?entity)) }
|
|
1039
|
-
UNION
|
|
1040
|
-
{ ?entity ?p <${entityUri}> . BIND("in" AS ?direction) FILTER(isIRI(?entity)) }
|
|
1041
|
-
}
|
|
1042
|
-
`
|
|
1043
|
-
}
|
|
1044
|
-
return this.querySource(query)
|
|
1045
|
-
}
|
|
687
|
+
_classifyIntent(prompt) {
|
|
688
|
+
const lowerPrompt = prompt.toLowerCase()
|
|
1046
689
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
|
1054
|
-
|
|
1055
|
-
SELECT ?class ?property ?domain ?range WHERE {
|
|
1056
|
-
{
|
|
1057
|
-
?class a owl:Class .
|
|
1058
|
-
} UNION {
|
|
1059
|
-
?property a owl:ObjectProperty .
|
|
1060
|
-
OPTIONAL { ?property rdfs:domain ?domain }
|
|
1061
|
-
OPTIONAL { ?property rdfs:range ?range }
|
|
1062
|
-
} UNION {
|
|
1063
|
-
?property a owl:DatatypeProperty .
|
|
1064
|
-
OPTIONAL { ?property rdfs:domain ?domain }
|
|
1065
|
-
OPTIONAL { ?property rdfs:range ?range }
|
|
690
|
+
for (const pattern of this.intentPatterns) {
|
|
691
|
+
if (pattern.patterns.some(p => lowerPrompt.includes(p))) {
|
|
692
|
+
return {
|
|
693
|
+
type: pattern.intent,
|
|
694
|
+
tools: pattern.tools,
|
|
695
|
+
confidence: 0.95
|
|
1066
696
|
}
|
|
1067
697
|
}
|
|
1068
|
-
`
|
|
1069
|
-
return this.querySource(query)
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
/**
|
|
1073
|
-
* Log access for audit
|
|
1074
|
-
*/
|
|
1075
|
-
_logAccess(operation, details) {
|
|
1076
|
-
this.accessLog.push({
|
|
1077
|
-
operation,
|
|
1078
|
-
details: details.slice(0, 200),
|
|
1079
|
-
timestamp: new Date().toISOString()
|
|
1080
|
-
})
|
|
1081
|
-
|
|
1082
|
-
// Keep only recent access log entries
|
|
1083
|
-
if (this.accessLog.length > 1000) {
|
|
1084
|
-
this.accessLog = this.accessLog.slice(-500)
|
|
1085
698
|
}
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
/**
|
|
1089
|
-
* Get access log
|
|
1090
|
-
*/
|
|
1091
|
-
getAccessLog(limit = 100) {
|
|
1092
|
-
return this.accessLog.slice(-limit)
|
|
1093
|
-
}
|
|
1094
699
|
|
|
1095
|
-
/**
|
|
1096
|
-
* Get graph URIs
|
|
1097
|
-
*/
|
|
1098
|
-
getGraphUris() {
|
|
1099
700
|
return {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
/**
|
|
1108
|
-
* MemoryManager - Unified memory retrieval with weighted scoring
|
|
1109
|
-
*
|
|
1110
|
-
* Orchestrates all memory layers with intelligent retrieval:
|
|
1111
|
-
* - Score = α × Recency + β × Relevance + γ × Importance
|
|
1112
|
-
* - α = 0.3 (time decay), β = 0.5 (semantic similarity), γ = 0.2 (access frequency)
|
|
1113
|
-
*/
|
|
1114
|
-
class MemoryManager {
|
|
1115
|
-
constructor(runtime, config = {}) {
|
|
1116
|
-
this.runtime = runtime
|
|
1117
|
-
|
|
1118
|
-
// Initialize memory layers
|
|
1119
|
-
this.working = new WorkingMemory(config.workingCapacity || 100)
|
|
1120
|
-
this.episodic = new EpisodicMemory(config.graphDb, config.episodicNamespace)
|
|
1121
|
-
this.longTerm = new LongTermMemory(config.graphDb, {
|
|
1122
|
-
sourceGraphUri: config.sourceGraphUri,
|
|
1123
|
-
agentGraphUri: config.agentGraphUri
|
|
1124
|
-
})
|
|
1125
|
-
|
|
1126
|
-
// Retrieval weights
|
|
1127
|
-
this.weights = config.weights || {
|
|
1128
|
-
recency: 0.3,
|
|
1129
|
-
relevance: 0.5,
|
|
1130
|
-
importance: 0.2
|
|
701
|
+
type: 'general_query',
|
|
702
|
+
tools: ['kg.sparql.query'],
|
|
703
|
+
confidence: 0.85
|
|
1131
704
|
}
|
|
1132
|
-
|
|
1133
|
-
// Access tracking for importance scoring
|
|
1134
|
-
this.accessCounts = new Map()
|
|
1135
|
-
this.retrievalHistory = []
|
|
1136
705
|
}
|
|
1137
706
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
episodic: [],
|
|
1145
|
-
longTerm: [],
|
|
1146
|
-
combined: []
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
// 1. Working Memory (always fast, in-memory)
|
|
1150
|
-
results.working = this._searchWorkingMemory(query)
|
|
1151
|
-
|
|
1152
|
-
// 2. Episodic Memory (past executions)
|
|
1153
|
-
if (options.includeEpisodic !== false) {
|
|
1154
|
-
const episodes = await this.episodic.getEpisodes(this.runtime.id, { limit: 20 })
|
|
1155
|
-
results.episodic = this._scoreEpisodicResults(episodes, query)
|
|
1156
|
-
}
|
|
707
|
+
_generatePlan(intent, prompt) {
|
|
708
|
+
const steps = intent.tools.map((tool, i) => ({
|
|
709
|
+
id: i + 1,
|
|
710
|
+
tool,
|
|
711
|
+
args: this._generateToolArgs(tool, intent, prompt)
|
|
712
|
+
}))
|
|
1157
713
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
score: 0.8, // Facts from source of truth have high base score
|
|
1164
|
-
source: 'longTerm'
|
|
1165
|
-
}))
|
|
714
|
+
return {
|
|
715
|
+
id: `plan_${Date.now()}`,
|
|
716
|
+
intent: intent.type,
|
|
717
|
+
steps,
|
|
718
|
+
type_chain: this._buildTypeChain(steps)
|
|
1166
719
|
}
|
|
1167
|
-
|
|
1168
|
-
// 4. Combine and rank
|
|
1169
|
-
results.combined = this._combineAndRank([
|
|
1170
|
-
...results.working.map(r => ({ ...r, source: 'working' })),
|
|
1171
|
-
...results.episodic,
|
|
1172
|
-
...results.longTerm
|
|
1173
|
-
])
|
|
1174
|
-
|
|
1175
|
-
// Track retrieval
|
|
1176
|
-
this._trackRetrieval(query, results)
|
|
1177
|
-
this.runtime.metrics.totalMemoryRetrievals++
|
|
1178
|
-
|
|
1179
|
-
return results
|
|
1180
720
|
}
|
|
1181
721
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
score: this._calculateScore(ctx.timestamp, 0.8, this._getAccessCount(ctx.id))
|
|
1197
|
-
})
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
// Search tool results
|
|
1202
|
-
for (const tr of this.working.toolResults) {
|
|
1203
|
-
if (tr.tool.toLowerCase().includes(queryLower) ||
|
|
1204
|
-
JSON.stringify(tr.result).toLowerCase().includes(queryLower)) {
|
|
1205
|
-
results.push({
|
|
1206
|
-
type: 'toolResult',
|
|
1207
|
-
data: tr,
|
|
1208
|
-
score: this._calculateScore(tr.timestamp, 0.7, 1)
|
|
1209
|
-
})
|
|
1210
|
-
}
|
|
722
|
+
_generateToolArgs(tool, intent, prompt) {
|
|
723
|
+
switch (tool) {
|
|
724
|
+
case 'kg.sparql.query':
|
|
725
|
+
return { query: this._generateSparql(intent, prompt) }
|
|
726
|
+
case 'kg.datalog.infer':
|
|
727
|
+
return { rules: this._selectRules(intent) }
|
|
728
|
+
case 'kg.embeddings.search':
|
|
729
|
+
return { text: prompt, k: 10, threshold: 0.7 }
|
|
730
|
+
case 'kg.motif.find':
|
|
731
|
+
return { pattern: this._selectMotifPattern(intent) }
|
|
732
|
+
case 'kg.graphframe.triangles':
|
|
733
|
+
return {}
|
|
734
|
+
default:
|
|
735
|
+
return {}
|
|
1211
736
|
}
|
|
1212
|
-
|
|
1213
|
-
return results.sort((a, b) => b.score - a.score)
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
/**
|
|
1217
|
-
* Score episodic results
|
|
1218
|
-
*/
|
|
1219
|
-
_scoreEpisodicResults(episodes, query) {
|
|
1220
|
-
return episodes.map(ep => {
|
|
1221
|
-
const recencyScore = this.episodic.calculateRecencyScore(ep.timestamp)
|
|
1222
|
-
const relevanceScore = this._calculateRelevance(ep.prompt, query)
|
|
1223
|
-
const importanceScore = this._getAccessCount(`ep_${ep.episode}`) / 10
|
|
1224
|
-
|
|
1225
|
-
return {
|
|
1226
|
-
...ep,
|
|
1227
|
-
source: 'episodic',
|
|
1228
|
-
score: this._calculateScore(ep.timestamp, relevanceScore, importanceScore)
|
|
1229
|
-
}
|
|
1230
|
-
}).sort((a, b) => b.score - a.score)
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
/**
|
|
1234
|
-
* Calculate weighted score
|
|
1235
|
-
*/
|
|
1236
|
-
_calculateScore(timestamp, relevance, accessCount) {
|
|
1237
|
-
const recency = this.episodic.calculateRecencyScore(timestamp)
|
|
1238
|
-
const importance = Math.min(accessCount / 10, 1)
|
|
1239
|
-
|
|
1240
|
-
return (
|
|
1241
|
-
this.weights.recency * recency +
|
|
1242
|
-
this.weights.relevance * relevance +
|
|
1243
|
-
this.weights.importance * importance
|
|
1244
|
-
)
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
/**
|
|
1248
|
-
* Calculate text relevance (simple term overlap)
|
|
1249
|
-
*/
|
|
1250
|
-
_calculateRelevance(text1, text2) {
|
|
1251
|
-
if (!text1 || !text2) return 0
|
|
1252
|
-
const words1 = new Set(text1.toLowerCase().split(/\s+/))
|
|
1253
|
-
const words2 = new Set(text2.toLowerCase().split(/\s+/))
|
|
1254
|
-
const intersection = [...words1].filter(w => words2.has(w))
|
|
1255
|
-
return intersection.length / Math.max(words1.size, words2.size)
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
/**
|
|
1259
|
-
* Get access count for importance scoring
|
|
1260
|
-
*/
|
|
1261
|
-
_getAccessCount(id) {
|
|
1262
|
-
return this.accessCounts.get(id) || 0
|
|
1263
737
|
}
|
|
1264
738
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
739
|
+
_generateSparql(intent, prompt) {
|
|
740
|
+
switch (intent.type) {
|
|
741
|
+
case 'detect_fraud':
|
|
742
|
+
return `PREFIX ins: <http://insurance.example.org/>
|
|
743
|
+
SELECT ?claim ?claimant ?amount ?riskScore
|
|
744
|
+
WHERE {
|
|
745
|
+
?claim a ins:Claim ;
|
|
746
|
+
ins:claimant ?claimant ;
|
|
747
|
+
ins:amount ?amount .
|
|
748
|
+
OPTIONAL { ?claimant ins:riskScore ?riskScore }
|
|
749
|
+
FILTER(BOUND(?riskScore) && ?riskScore > 0.7)
|
|
750
|
+
}
|
|
751
|
+
ORDER BY DESC(?riskScore)
|
|
752
|
+
LIMIT 100`
|
|
1271
753
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
}
|
|
754
|
+
case 'academic_query':
|
|
755
|
+
if (prompt.toLowerCase().includes('professor')) {
|
|
756
|
+
return `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
757
|
+
SELECT ?professor ?name ?dept
|
|
758
|
+
WHERE {
|
|
759
|
+
?professor a ub:Professor .
|
|
760
|
+
OPTIONAL { ?professor ub:name ?name }
|
|
761
|
+
OPTIONAL { ?professor ub:worksFor ?dept }
|
|
762
|
+
}
|
|
763
|
+
LIMIT 100`
|
|
764
|
+
}
|
|
765
|
+
return `SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 100`
|
|
1280
766
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
*/
|
|
1284
|
-
_trackRetrieval(query, results) {
|
|
1285
|
-
this.retrievalHistory.push({
|
|
1286
|
-
query,
|
|
1287
|
-
timestamp: Date.now(),
|
|
1288
|
-
counts: {
|
|
1289
|
-
working: results.working.length,
|
|
1290
|
-
episodic: results.episodic.length,
|
|
1291
|
-
longTerm: results.longTerm.length
|
|
1292
|
-
}
|
|
1293
|
-
})
|
|
767
|
+
case 'aggregate':
|
|
768
|
+
return `SELECT (COUNT(*) as ?count) WHERE { ?s ?p ?o }`
|
|
1294
769
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
if (r.data?.id) {
|
|
1298
|
-
this._incrementAccess(r.data.id)
|
|
1299
|
-
}
|
|
770
|
+
default:
|
|
771
|
+
return `SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 100`
|
|
1300
772
|
}
|
|
1301
773
|
}
|
|
1302
774
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
* Add to working memory
|
|
1312
|
-
*/
|
|
1313
|
-
addToWorking(item) {
|
|
1314
|
-
this.working.addContext(item)
|
|
1315
|
-
return this
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
/**
|
|
1319
|
-
* Get memory statistics
|
|
1320
|
-
*/
|
|
1321
|
-
getStats() {
|
|
1322
|
-
return {
|
|
1323
|
-
working: {
|
|
1324
|
-
contextSize: this.working.context.length,
|
|
1325
|
-
bindingsCount: this.working.bindings.size,
|
|
1326
|
-
toolResultsCount: this.working.toolResults.length
|
|
1327
|
-
},
|
|
1328
|
-
episodic: {
|
|
1329
|
-
episodeCount: this.episodic.getEpisodeCount()
|
|
1330
|
-
},
|
|
1331
|
-
longTerm: {
|
|
1332
|
-
accessLogSize: this.longTerm.accessLog.length
|
|
1333
|
-
},
|
|
1334
|
-
retrieval: {
|
|
1335
|
-
totalRetrievals: this.retrievalHistory.length,
|
|
1336
|
-
uniqueAccessedItems: this.accessCounts.size
|
|
1337
|
-
},
|
|
1338
|
-
weights: this.weights
|
|
775
|
+
_selectRules(intent) {
|
|
776
|
+
switch (intent.type) {
|
|
777
|
+
case 'detect_fraud':
|
|
778
|
+
return ['potential_fraud', 'collusion_pattern', 'circular_payment']
|
|
779
|
+
case 'find_patterns':
|
|
780
|
+
return ['circular_payment', 'collusion_pattern']
|
|
781
|
+
default:
|
|
782
|
+
return []
|
|
1339
783
|
}
|
|
1340
784
|
}
|
|
1341
785
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
// ==========================================================================
|
|
1351
|
-
// SEMANTIC HASHING (LSH - Locality Sensitive Hashing)
|
|
1352
|
-
// Research: SimHash (Charikar 2002), Semantic Hashing (Salakhutdinov & Hinton 2009)
|
|
1353
|
-
// ==========================================================================
|
|
1354
|
-
|
|
1355
|
-
/**
|
|
1356
|
-
* Generate semantic hash using LSH with random hyperplane projections
|
|
1357
|
-
* 384-dim embeddings → 64 hyperplanes → 64-bit semantic hash
|
|
1358
|
-
*
|
|
1359
|
-
* @param {string} text - Text to hash semantically
|
|
1360
|
-
* @returns {string} Semantic hash in format "semhash:xxx-xxx-xxx"
|
|
1361
|
-
*/
|
|
1362
|
-
generateSemanticHash(text) {
|
|
1363
|
-
// Normalize and tokenize
|
|
1364
|
-
const tokens = text.toLowerCase()
|
|
1365
|
-
.replace(/[^\w\s]/g, '')
|
|
1366
|
-
.split(/\s+/)
|
|
1367
|
-
.filter(t => t.length > 2)
|
|
1368
|
-
|
|
1369
|
-
// Generate hash components from key terms
|
|
1370
|
-
const hashParts = []
|
|
1371
|
-
|
|
1372
|
-
// Extract entity references (Provider, Claim, Policy patterns)
|
|
1373
|
-
const entityPattern = /([A-Z][a-z]+)[:\s]?([A-Z0-9]+)/g
|
|
1374
|
-
const entities = [...text.matchAll(entityPattern)]
|
|
1375
|
-
for (const match of entities) {
|
|
1376
|
-
hashParts.push(`${match[1].toLowerCase()}-${match[2].toLowerCase()}`)
|
|
786
|
+
_selectMotifPattern(intent) {
|
|
787
|
+
switch (intent.type) {
|
|
788
|
+
case 'find_patterns':
|
|
789
|
+
return '(a)-[paid]->(b); (b)-[paid]->(c); (c)-[paid]->(a)'
|
|
790
|
+
default:
|
|
791
|
+
return '(a)-[]->(b)'
|
|
1377
792
|
}
|
|
1378
|
-
|
|
1379
|
-
// Extract action/intent keywords
|
|
1380
|
-
const actionWords = ['fraud', 'detect', 'analyze', 'find', 'claim', 'deny', 'approve', 'risk', 'pattern', 'investigation']
|
|
1381
|
-
const foundActions = tokens.filter(t => actionWords.some(a => t.includes(a)))
|
|
1382
|
-
hashParts.push(...foundActions.slice(0, 3))
|
|
1383
|
-
|
|
1384
|
-
// Combine into semantic hash
|
|
1385
|
-
const semanticParts = hashParts.slice(0, 5).join('-') || 'general-query'
|
|
1386
|
-
return `semhash:${semanticParts}`
|
|
1387
793
|
}
|
|
1388
794
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
* Uses Jaccard similarity on hash components
|
|
1392
|
-
*/
|
|
1393
|
-
semanticHashMatch(hash1, hash2, threshold = 0.6) {
|
|
1394
|
-
const parts1 = new Set(hash1.replace('semhash:', '').split('-'))
|
|
1395
|
-
const parts2 = new Set(hash2.replace('semhash:', '').split('-'))
|
|
1396
|
-
|
|
1397
|
-
const intersection = [...parts1].filter(p => parts2.has(p)).length
|
|
1398
|
-
const union = new Set([...parts1, ...parts2]).size
|
|
1399
|
-
|
|
1400
|
-
return (intersection / union) >= threshold
|
|
795
|
+
_buildTypeChain(steps) {
|
|
796
|
+
return steps.map(s => s.tool).join(' -> ')
|
|
1401
797
|
}
|
|
1402
798
|
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
// ==========================================================================
|
|
1406
|
-
|
|
1407
|
-
/**
|
|
1408
|
-
* Recall memories enriched with Knowledge Graph context
|
|
1409
|
-
* Typed API that generates optimized SPARQL internally
|
|
1410
|
-
*
|
|
1411
|
-
* @param {Object} options - Recall options
|
|
1412
|
-
* @param {string} options.query - Natural language query
|
|
1413
|
-
* @param {Object} options.kgFilter - Optional KG filter {predicate, operator, value}
|
|
1414
|
-
* @param {number} options.limit - Max results (default 10)
|
|
1415
|
-
* @returns {Promise<Array>} Enriched memory results with KG context
|
|
1416
|
-
*/
|
|
1417
|
-
async recallWithKG(options = {}) {
|
|
1418
|
-
const { query, kgFilter, limit = 10 } = options
|
|
1419
|
-
|
|
1420
|
-
// Generate semantic hash for caching
|
|
1421
|
-
const semanticHash = this.generateSemanticHash(query)
|
|
799
|
+
async _executePlan(plan, trace) {
|
|
800
|
+
const results = []
|
|
1422
801
|
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
if (cached) {
|
|
1426
|
-
this.runtime.metrics.semanticCacheHits = (this.runtime.metrics.semanticCacheHits || 0) + 1
|
|
1427
|
-
return { ...cached, fromCache: true, semanticHash }
|
|
1428
|
-
}
|
|
802
|
+
for (const step of plan.steps) {
|
|
803
|
+
this.sandbox.consumeFuel(100)
|
|
1429
804
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
805
|
+
try {
|
|
806
|
+
const result = await this._executeTool(step.tool, step.args)
|
|
807
|
+
results.push({ step: step.id, tool: step.tool, result, success: true })
|
|
1433
808
|
|
|
1434
|
-
|
|
1435
|
-
|
|
809
|
+
if (step.tool === 'kg.sparql.query') {
|
|
810
|
+
trace.addSparql(step.args.query, result)
|
|
811
|
+
}
|
|
1436
812
|
|
|
1437
|
-
|
|
1438
|
-
let kgContext = []
|
|
1439
|
-
if (this.runtime.graphDb && sparql) {
|
|
1440
|
-
try {
|
|
1441
|
-
const results = this.runtime.graphDb.querySelect(sparql)
|
|
1442
|
-
kgContext = results.map(r => ({
|
|
1443
|
-
...r.bindings,
|
|
1444
|
-
source: 'knowledgeGraph'
|
|
1445
|
-
}))
|
|
813
|
+
this.sandbox.log(step.tool, step.args, result, 'OK')
|
|
1446
814
|
} catch (err) {
|
|
1447
|
-
|
|
1448
|
-
|
|
815
|
+
results.push({ step: step.id, tool: step.tool, error: err.message, success: false })
|
|
816
|
+
this.sandbox.log(step.tool, step.args, null, 'ERROR')
|
|
1449
817
|
}
|
|
1450
818
|
}
|
|
1451
819
|
|
|
1452
|
-
|
|
1453
|
-
const enrichedResults = scoredEpisodes.slice(0, limit).map(ep => {
|
|
1454
|
-
const relatedKG = kgContext.filter(kg =>
|
|
1455
|
-
JSON.stringify(kg).toLowerCase().includes(
|
|
1456
|
-
ep.prompt?.toLowerCase().split(' ').slice(0, 3).join(' ') || ''
|
|
1457
|
-
)
|
|
1458
|
-
)
|
|
1459
|
-
|
|
1460
|
-
return {
|
|
1461
|
-
episode: ep.episode,
|
|
1462
|
-
finding: ep.prompt,
|
|
1463
|
-
timestamp: ep.timestamp,
|
|
1464
|
-
score: ep.score,
|
|
1465
|
-
kgContext: relatedKG.length > 0 ? relatedKG : null,
|
|
1466
|
-
semanticHash
|
|
1467
|
-
}
|
|
1468
|
-
})
|
|
1469
|
-
|
|
1470
|
-
// Cache result
|
|
1471
|
-
this._storeSemanticCache(semanticHash, enrichedResults)
|
|
1472
|
-
|
|
1473
|
-
return enrichedResults
|
|
820
|
+
return results
|
|
1474
821
|
}
|
|
1475
822
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
*/
|
|
1480
|
-
_buildMemoryKGQuery(episodes, kgFilter) {
|
|
1481
|
-
if (!episodes.length) return null
|
|
1482
|
-
|
|
1483
|
-
// Extract entity URIs from episodes
|
|
1484
|
-
const entityPattern = /([A-Z][a-z]+):?([A-Z0-9]+)/g
|
|
1485
|
-
const entities = new Set()
|
|
1486
|
-
for (const ep of episodes) {
|
|
1487
|
-
const matches = ep.prompt?.matchAll(entityPattern) || []
|
|
1488
|
-
for (const match of matches) {
|
|
1489
|
-
entities.add(`<http://example.org/${match[1]}/${match[2]}>`)
|
|
1490
|
-
}
|
|
823
|
+
async _executeTool(tool, args) {
|
|
824
|
+
if (!this.sandbox.hasCapability('ExecuteTool')) {
|
|
825
|
+
throw new Error('Missing ExecuteTool capability')
|
|
1491
826
|
}
|
|
1492
827
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
828
|
+
switch (tool) {
|
|
829
|
+
case 'kg.sparql.query':
|
|
830
|
+
return this._executeSparql(args.query)
|
|
831
|
+
case 'kg.datalog.infer':
|
|
832
|
+
return this._executeDatalog(args.rules)
|
|
833
|
+
case 'kg.embeddings.search':
|
|
834
|
+
return this._executeEmbeddingSearch(args)
|
|
835
|
+
case 'kg.motif.find':
|
|
836
|
+
return this._executeMotifFind(args)
|
|
837
|
+
case 'kg.graphframe.triangles':
|
|
838
|
+
return this._executeTriangleCount()
|
|
839
|
+
default:
|
|
840
|
+
return { status: 'unknown_tool' }
|
|
1499
841
|
}
|
|
1500
|
-
|
|
1501
|
-
return `
|
|
1502
|
-
PREFIX am: <https://gonnect.ai/ontology/agent-memory#>
|
|
1503
|
-
|
|
1504
|
-
SELECT ?entity ?predicate ?value WHERE {
|
|
1505
|
-
VALUES ?entity { ${entityValues} }
|
|
1506
|
-
?entity ?predicate ?value .
|
|
1507
|
-
${filterClause}
|
|
1508
|
-
} LIMIT 100`
|
|
1509
842
|
}
|
|
1510
843
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
*/
|
|
1515
|
-
_semanticCache = new Map()
|
|
1516
|
-
|
|
1517
|
-
_checkSemanticCache(hash) {
|
|
1518
|
-
// Check for exact match
|
|
1519
|
-
if (this._semanticCache.has(hash)) {
|
|
1520
|
-
return this._semanticCache.get(hash)
|
|
844
|
+
_executeSparql(query) {
|
|
845
|
+
if (!this.kg || !this.kg.querySelect) {
|
|
846
|
+
return []
|
|
1521
847
|
}
|
|
1522
848
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
}
|
|
849
|
+
try {
|
|
850
|
+
const results = this.kg.querySelect(query)
|
|
851
|
+
return results.map(r => r.bindings || r)
|
|
852
|
+
} catch (err) {
|
|
853
|
+
return { error: err.message }
|
|
1528
854
|
}
|
|
1529
|
-
|
|
1530
|
-
return null
|
|
1531
855
|
}
|
|
1532
856
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
const
|
|
1537
|
-
|
|
857
|
+
_executeDatalog(ruleNames) {
|
|
858
|
+
const results = []
|
|
859
|
+
for (const name of ruleNames) {
|
|
860
|
+
const rule = this.rules.getRule(name)
|
|
861
|
+
if (rule) {
|
|
862
|
+
results.push({
|
|
863
|
+
rule: name,
|
|
864
|
+
description: rule.description,
|
|
865
|
+
applied: true
|
|
866
|
+
})
|
|
867
|
+
}
|
|
1538
868
|
}
|
|
1539
|
-
|
|
869
|
+
return results
|
|
1540
870
|
}
|
|
1541
|
-
}
|
|
1542
871
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
/**
|
|
1548
|
-
* GovernancePolicy - Defines access control and behavioral policies
|
|
1549
|
-
*/
|
|
1550
|
-
class GovernancePolicy {
|
|
1551
|
-
constructor(config = {}) {
|
|
1552
|
-
this.name = config.name || 'default-policy'
|
|
1553
|
-
this.version = config.version || '1.0.0'
|
|
1554
|
-
|
|
1555
|
-
// Capability definitions
|
|
1556
|
-
this.capabilities = new Set(config.capabilities || [
|
|
1557
|
-
'ReadKG',
|
|
1558
|
-
'WriteKG',
|
|
1559
|
-
'ExecuteTool',
|
|
1560
|
-
'AccessMemory',
|
|
1561
|
-
'ModifyMemory'
|
|
1562
|
-
])
|
|
1563
|
-
|
|
1564
|
-
// Resource limits
|
|
1565
|
-
this.limits = config.limits || {
|
|
1566
|
-
maxExecutionTimeMs: 60000,
|
|
1567
|
-
maxMemoryMB: 256,
|
|
1568
|
-
maxToolCalls: 100,
|
|
1569
|
-
maxGraphQueries: 1000
|
|
872
|
+
_executeEmbeddingSearch(args) {
|
|
873
|
+
if (!this.embeddings || !this.embeddings.search) {
|
|
874
|
+
return []
|
|
1570
875
|
}
|
|
1571
876
|
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
logGraphQueries: true,
|
|
1577
|
-
retentionDays: 90
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
// Behavioral constraints
|
|
1581
|
-
this.constraints = config.constraints || {
|
|
1582
|
-
allowExternalApi: false,
|
|
1583
|
-
allowFileAccess: false,
|
|
1584
|
-
requireApprovalForWrites: false
|
|
877
|
+
try {
|
|
878
|
+
return this.embeddings.search(args.text, args.k, args.threshold)
|
|
879
|
+
} catch (err) {
|
|
880
|
+
return { error: err.message }
|
|
1585
881
|
}
|
|
1586
882
|
}
|
|
1587
883
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
hasCapability(capability) {
|
|
1592
|
-
return this.capabilities.has(capability)
|
|
884
|
+
_executeMotifFind(args) {
|
|
885
|
+
// Motif finding would use GraphFrame.find()
|
|
886
|
+
return { pattern: args.pattern, matches: [] }
|
|
1593
887
|
}
|
|
1594
888
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
grantCapability(capability) {
|
|
1599
|
-
this.capabilities.add(capability)
|
|
1600
|
-
return this
|
|
889
|
+
_executeTriangleCount() {
|
|
890
|
+
// Triangle counting would use GraphFrame.triangleCount()
|
|
891
|
+
return { count: 0 }
|
|
1601
892
|
}
|
|
1602
893
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
*/
|
|
1606
|
-
revokeCapability(capability) {
|
|
1607
|
-
this.capabilities.delete(capability)
|
|
1608
|
-
return this
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
/**
|
|
1612
|
-
* Check resource limit
|
|
1613
|
-
*/
|
|
1614
|
-
checkLimit(resource, current) {
|
|
1615
|
-
const limit = this.limits[resource]
|
|
1616
|
-
return limit === undefined || current <= limit
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
/**
|
|
1620
|
-
* Export policy as JSON
|
|
1621
|
-
*/
|
|
1622
|
-
export() {
|
|
1623
|
-
return {
|
|
1624
|
-
name: this.name,
|
|
1625
|
-
version: this.version,
|
|
1626
|
-
capabilities: [...this.capabilities],
|
|
1627
|
-
limits: this.limits,
|
|
1628
|
-
audit: this.audit,
|
|
1629
|
-
constraints: this.constraints
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
/**
|
|
1635
|
-
* GovernanceEngine - Enforces policies and manages capability grants
|
|
1636
|
-
*/
|
|
1637
|
-
class GovernanceEngine {
|
|
1638
|
-
constructor(policy = null) {
|
|
1639
|
-
this.policy = policy || new GovernancePolicy()
|
|
1640
|
-
this.auditLog = []
|
|
1641
|
-
this.denials = []
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
/**
|
|
1645
|
-
* Authorize an action
|
|
1646
|
-
*/
|
|
1647
|
-
authorize(action, context = {}) {
|
|
1648
|
-
const result = {
|
|
1649
|
-
allowed: false,
|
|
1650
|
-
reason: '',
|
|
1651
|
-
capability: action.capability,
|
|
1652
|
-
timestamp: new Date().toISOString()
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
// Check capability
|
|
1656
|
-
if (action.capability && !this.policy.hasCapability(action.capability)) {
|
|
1657
|
-
result.reason = `Missing capability: ${action.capability}`
|
|
1658
|
-
this._logDenial(action, result.reason)
|
|
1659
|
-
return result
|
|
1660
|
-
}
|
|
894
|
+
async _applyRules(intent, results, trace) {
|
|
895
|
+
const inferences = []
|
|
1661
896
|
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
897
|
+
if (intent.type === 'detect_fraud') {
|
|
898
|
+
const selectedRules = this._selectRules(intent)
|
|
899
|
+
for (const ruleName of selectedRules) {
|
|
900
|
+
const rule = this.rules.getRule(ruleName)
|
|
901
|
+
if (rule) {
|
|
902
|
+
inferences.push({
|
|
903
|
+
rule: ruleName,
|
|
904
|
+
description: rule.description,
|
|
905
|
+
head: rule.head,
|
|
906
|
+
body: rule.body
|
|
907
|
+
})
|
|
908
|
+
trace.addRule(ruleName, rule.body, rule.head)
|
|
909
|
+
}
|
|
1668
910
|
}
|
|
1669
911
|
}
|
|
1670
912
|
|
|
1671
|
-
|
|
1672
|
-
if (action.type === 'externalApi' && !this.policy.constraints.allowExternalApi) {
|
|
1673
|
-
result.reason = 'External API access not allowed'
|
|
1674
|
-
this._logDenial(action, result.reason)
|
|
1675
|
-
return result
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
result.allowed = true
|
|
1679
|
-
result.reason = 'Authorized'
|
|
1680
|
-
this._logAudit(action, result)
|
|
1681
|
-
|
|
1682
|
-
return result
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
/**
|
|
1686
|
-
* Log audit entry
|
|
1687
|
-
*/
|
|
1688
|
-
_logAudit(action, result) {
|
|
1689
|
-
if (this.policy.audit.logAllToolCalls ||
|
|
1690
|
-
(this.policy.audit.logMemoryAccess && action.type === 'memory') ||
|
|
1691
|
-
(this.policy.audit.logGraphQueries && action.type === 'graphQuery')) {
|
|
1692
|
-
this.auditLog.push({
|
|
1693
|
-
action,
|
|
1694
|
-
result,
|
|
1695
|
-
timestamp: new Date().toISOString()
|
|
1696
|
-
})
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
|
-
/**
|
|
1701
|
-
* Log denial
|
|
1702
|
-
*/
|
|
1703
|
-
_logDenial(action, reason) {
|
|
1704
|
-
this.denials.push({
|
|
1705
|
-
action,
|
|
1706
|
-
reason,
|
|
1707
|
-
timestamp: new Date().toISOString()
|
|
1708
|
-
})
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
/**
|
|
1712
|
-
* Get audit log
|
|
1713
|
-
*/
|
|
1714
|
-
getAuditLog(limit = 100) {
|
|
1715
|
-
return this.auditLog.slice(-limit)
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
/**
|
|
1719
|
-
* Get denials
|
|
1720
|
-
*/
|
|
1721
|
-
getDenials(limit = 50) {
|
|
1722
|
-
return this.denials.slice(-limit)
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
/**
|
|
1726
|
-
* Update policy
|
|
1727
|
-
*/
|
|
1728
|
-
setPolicy(policy) {
|
|
1729
|
-
this.policy = policy
|
|
1730
|
-
return this
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
// ============================================================================
|
|
1735
|
-
// SCOPE LAYER (Namespace Isolation & Resource Limits)
|
|
1736
|
-
// ============================================================================
|
|
1737
|
-
|
|
1738
|
-
/**
|
|
1739
|
-
* AgentScope - Defines namespace and resource boundaries for agents
|
|
1740
|
-
*/
|
|
1741
|
-
class AgentScope {
|
|
1742
|
-
constructor(config = {}) {
|
|
1743
|
-
this.id = config.id || `scope_${crypto.randomUUID()}`
|
|
1744
|
-
this.name = config.name || 'default-scope'
|
|
1745
|
-
|
|
1746
|
-
// Namespace configuration
|
|
1747
|
-
this.namespace = config.namespace || {
|
|
1748
|
-
prefix: 'http://hypermind.ai/agent/',
|
|
1749
|
-
graphUri: `http://hypermind.ai/agent/${this.id}/`,
|
|
1750
|
-
allowedGraphs: ['*'], // '*' means all, or list specific graph URIs
|
|
1751
|
-
deniedGraphs: []
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
// Resource limits
|
|
1755
|
-
this.resources = {
|
|
1756
|
-
maxMemoryMB: config.maxMemoryMB || 256,
|
|
1757
|
-
maxExecutionTimeMs: config.maxExecutionTimeMs || 60000,
|
|
1758
|
-
maxToolCalls: config.maxToolCalls || 100,
|
|
1759
|
-
maxGraphQueries: config.maxGraphQueries || 1000,
|
|
1760
|
-
maxEpisodes: config.maxEpisodes || 10000
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
// Current usage tracking
|
|
1764
|
-
this.usage = {
|
|
1765
|
-
memoryMB: 0,
|
|
1766
|
-
executionTimeMs: 0,
|
|
1767
|
-
toolCalls: 0,
|
|
1768
|
-
graphQueries: 0,
|
|
1769
|
-
episodes: 0
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
// Isolation settings
|
|
1773
|
-
this.isolation = {
|
|
1774
|
-
shareMemory: config.shareMemory || false,
|
|
1775
|
-
shareEpisodes: config.shareEpisodes || false,
|
|
1776
|
-
parentScope: config.parentScope || null
|
|
1777
|
-
}
|
|
913
|
+
return inferences
|
|
1778
914
|
}
|
|
1779
915
|
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
isGraphAllowed(graphUri) {
|
|
1784
|
-
// Check denied first
|
|
1785
|
-
if (this.namespace.deniedGraphs.includes(graphUri)) {
|
|
1786
|
-
return false
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
// Check allowed
|
|
1790
|
-
if (this.namespace.allowedGraphs.includes('*')) {
|
|
1791
|
-
return true
|
|
1792
|
-
}
|
|
916
|
+
_buildProofDAG(plan, results, inferences, trace) {
|
|
917
|
+
// Build proof tree showing how answer was derived
|
|
918
|
+
const children = []
|
|
1793
919
|
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
920
|
+
// Add SPARQL results as axioms
|
|
921
|
+
for (const r of results) {
|
|
922
|
+
if (r.success && r.tool === 'kg.sparql.query') {
|
|
923
|
+
children.push(new ProofNode(
|
|
924
|
+
'sparql',
|
|
925
|
+
r.result,
|
|
926
|
+
`Executed SPARQL query`,
|
|
927
|
+
[]
|
|
928
|
+
))
|
|
1797
929
|
}
|
|
1798
|
-
return graphUri === pattern
|
|
1799
|
-
})
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
/**
|
|
1803
|
-
* Track resource usage
|
|
1804
|
-
*/
|
|
1805
|
-
trackUsage(resource, amount) {
|
|
1806
|
-
if (this.usage[resource] !== undefined) {
|
|
1807
|
-
this.usage[resource] += amount
|
|
1808
930
|
}
|
|
1809
|
-
return this
|
|
1810
|
-
}
|
|
1811
931
|
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
/**
|
|
1821
|
-
* Get remaining resources
|
|
1822
|
-
*/
|
|
1823
|
-
getRemainingResources() {
|
|
1824
|
-
const remaining = {}
|
|
1825
|
-
for (const [key, limit] of Object.entries(this.resources)) {
|
|
1826
|
-
// Convert maxToolCalls -> toolCalls (preserve camelCase)
|
|
1827
|
-
const withoutMax = key.replace(/^max/, '')
|
|
1828
|
-
const usageKey = withoutMax.charAt(0).toLowerCase() + withoutMax.slice(1)
|
|
1829
|
-
remaining[usageKey] = limit - (this.usage[usageKey] || 0)
|
|
932
|
+
// Add rule applications as inferences
|
|
933
|
+
for (const inf of inferences) {
|
|
934
|
+
children.push(new ProofNode(
|
|
935
|
+
'rule',
|
|
936
|
+
inf,
|
|
937
|
+
`Applied rule: ${inf.description}`,
|
|
938
|
+
inf.body.map(b => new ProofNode('premise', b, `Premise: ${JSON.stringify(b)}`))
|
|
939
|
+
))
|
|
1830
940
|
}
|
|
1831
|
-
return remaining
|
|
1832
|
-
}
|
|
1833
941
|
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
}
|
|
1841
|
-
return this
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
/**
|
|
1845
|
-
* Get scoped graph URI for agent
|
|
1846
|
-
*/
|
|
1847
|
-
getScopedGraphUri(suffix = '') {
|
|
1848
|
-
return `${this.namespace.graphUri}${suffix}`
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
/**
|
|
1852
|
-
* Export scope configuration
|
|
1853
|
-
*/
|
|
1854
|
-
export() {
|
|
1855
|
-
return {
|
|
1856
|
-
id: this.id,
|
|
1857
|
-
name: this.name,
|
|
1858
|
-
namespace: this.namespace,
|
|
1859
|
-
resources: this.resources,
|
|
1860
|
-
usage: this.usage,
|
|
1861
|
-
isolation: this.isolation
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
// ============================================================================
|
|
1867
|
-
// AGENT BUILDER (Fluent Composition Pattern)
|
|
1868
|
-
// ============================================================================
|
|
1869
|
-
|
|
1870
|
-
/**
|
|
1871
|
-
* ComposedAgent - Agent built from composed tools, planner, and sandbox
|
|
1872
|
-
*/
|
|
1873
|
-
class ComposedAgent {
|
|
1874
|
-
constructor(spec) {
|
|
1875
|
-
this.name = spec.name
|
|
1876
|
-
this.tools = spec.tools
|
|
1877
|
-
this.planner = spec.planner
|
|
1878
|
-
this.sandbox = spec.sandbox
|
|
1879
|
-
this.hooks = spec.hooks || []
|
|
1880
|
-
this.conversationHistory = []
|
|
942
|
+
return new ProofNode(
|
|
943
|
+
'inference',
|
|
944
|
+
{ plan: plan.id, intent: plan.intent },
|
|
945
|
+
`Derived answer via ${plan.steps.length} steps`,
|
|
946
|
+
children
|
|
947
|
+
)
|
|
1881
948
|
}
|
|
1882
949
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
const plan = await this.planner.plan(prompt, {
|
|
1887
|
-
history: this.conversationHistory
|
|
1888
|
-
})
|
|
1889
|
-
|
|
1890
|
-
this._fireHooks('afterPlan', { plan })
|
|
950
|
+
_formatAnswer(results, inferences, intent) {
|
|
951
|
+
const sparqlResults = results.filter(r => r.tool === 'kg.sparql.query' && r.success)
|
|
952
|
+
const totalResults = sparqlResults.reduce((sum, r) => sum + (Array.isArray(r.result) ? r.result.length : 0), 0)
|
|
1891
953
|
|
|
1892
|
-
|
|
1893
|
-
const results = []
|
|
1894
|
-
|
|
1895
|
-
for (const step of plan.steps) {
|
|
1896
|
-
this._fireHooks('beforeExecute', { step })
|
|
954
|
+
let answer = `Found ${totalResults} results`
|
|
1897
955
|
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
results.push({ step, result, status: 'success' })
|
|
1901
|
-
this._fireHooks('afterExecute', { step, result })
|
|
1902
|
-
} catch (error) {
|
|
1903
|
-
results.push({ step, error: error.message, status: 'failed' })
|
|
1904
|
-
this._fireHooks('onError', { step, error })
|
|
1905
|
-
}
|
|
956
|
+
if (inferences.length > 0) {
|
|
957
|
+
answer += ` using ${inferences.length} reasoning rules`
|
|
1906
958
|
}
|
|
1907
959
|
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
this.conversationHistory.push({ prompt, plan, results, witness })
|
|
1911
|
-
|
|
1912
|
-
return {
|
|
1913
|
-
response: `Executed ${results.length} tools`,
|
|
1914
|
-
plan,
|
|
1915
|
-
results,
|
|
1916
|
-
witness,
|
|
1917
|
-
metrics: this.sandbox.getMetrics()
|
|
960
|
+
if (intent.type === 'detect_fraud' && totalResults > 0) {
|
|
961
|
+
answer = `Detected ${totalResults} potential fraud cases using ${inferences.length} detection rules`
|
|
1918
962
|
}
|
|
1919
|
-
}
|
|
1920
963
|
|
|
1921
|
-
|
|
1922
|
-
for (const hook of this.hooks) {
|
|
1923
|
-
if (hook.event === event) hook.handler(data)
|
|
1924
|
-
}
|
|
964
|
+
return answer
|
|
1925
965
|
}
|
|
966
|
+
}
|
|
1926
967
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
timestamp: new Date().toISOString(),
|
|
1931
|
-
agent: this.name,
|
|
1932
|
-
model: this.planner.model,
|
|
1933
|
-
plan: { id: plan.id, steps: plan.steps.length, confidence: plan.confidence },
|
|
1934
|
-
execution: {
|
|
1935
|
-
tool_calls: results.map(r => ({
|
|
1936
|
-
tool: r.step.tool,
|
|
1937
|
-
status: r.status
|
|
1938
|
-
}))
|
|
1939
|
-
},
|
|
1940
|
-
sandbox_metrics: this.sandbox.getMetrics(),
|
|
1941
|
-
audit_log: this.sandbox.getAuditLog()
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
const proofHash = crypto
|
|
1945
|
-
.createHash('sha256')
|
|
1946
|
-
.update(JSON.stringify(witnessData))
|
|
1947
|
-
.digest('hex')
|
|
968
|
+
// ============================================================================
|
|
969
|
+
// AGENT STATE MACHINE
|
|
970
|
+
// ============================================================================
|
|
1948
971
|
|
|
1949
|
-
|
|
1950
|
-
|
|
972
|
+
/**
|
|
973
|
+
* AgentState - State machine for agent lifecycle
|
|
974
|
+
*/
|
|
975
|
+
const AgentState = {
|
|
976
|
+
IDLE: 'IDLE',
|
|
977
|
+
READY: 'READY',
|
|
978
|
+
PLANNING: 'PLANNING',
|
|
979
|
+
EXECUTING: 'EXECUTING',
|
|
980
|
+
WAITING: 'WAITING',
|
|
981
|
+
ERROR: 'ERROR',
|
|
982
|
+
TERMINATED: 'TERMINATED'
|
|
1951
983
|
}
|
|
1952
984
|
|
|
985
|
+
// ============================================================================
|
|
986
|
+
// AGENT RUNTIME
|
|
987
|
+
// ============================================================================
|
|
988
|
+
|
|
1953
989
|
/**
|
|
1954
|
-
*
|
|
990
|
+
* AgentRuntime - Runtime context for agent execution
|
|
1955
991
|
*/
|
|
1956
|
-
class
|
|
1957
|
-
constructor(
|
|
1958
|
-
this.
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
992
|
+
class AgentRuntime {
|
|
993
|
+
constructor(config = {}) {
|
|
994
|
+
this.id = crypto.randomUUID()
|
|
995
|
+
this.name = config.name || 'agent'
|
|
996
|
+
this.model = config.model || 'mock'
|
|
997
|
+
this.tools = config.tools || []
|
|
998
|
+
this.state = AgentState.IDLE
|
|
999
|
+
this.memoryCapacity = config.memoryCapacity || 100
|
|
1000
|
+
this.episodeLimit = config.episodeLimit || 1000
|
|
1001
|
+
this.createdAt = new Date().toISOString()
|
|
1002
|
+
this.stateHistory = [{ state: this.state, timestamp: Date.now() }]
|
|
1003
|
+
this.executions = []
|
|
1004
|
+
this.currentExecution = null
|
|
1965
1005
|
}
|
|
1966
1006
|
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1007
|
+
transitionTo(newState) {
|
|
1008
|
+
const validTransitions = {
|
|
1009
|
+
[AgentState.IDLE]: [AgentState.READY, AgentState.TERMINATED],
|
|
1010
|
+
[AgentState.READY]: [AgentState.PLANNING, AgentState.EXECUTING, AgentState.TERMINATED],
|
|
1011
|
+
[AgentState.PLANNING]: [AgentState.EXECUTING, AgentState.WAITING, AgentState.ERROR],
|
|
1012
|
+
[AgentState.EXECUTING]: [AgentState.READY, AgentState.WAITING, AgentState.ERROR],
|
|
1013
|
+
[AgentState.WAITING]: [AgentState.READY, AgentState.PLANNING, AgentState.ERROR],
|
|
1014
|
+
[AgentState.ERROR]: [AgentState.READY, AgentState.TERMINATED],
|
|
1015
|
+
[AgentState.TERMINATED]: []
|
|
1973
1016
|
}
|
|
1974
|
-
return this
|
|
1975
|
-
}
|
|
1976
1017
|
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
}
|
|
1018
|
+
if (!validTransitions[this.state]?.includes(newState)) {
|
|
1019
|
+
throw new Error(`Invalid state transition: ${this.state} -> ${newState}`)
|
|
1020
|
+
}
|
|
1981
1021
|
|
|
1982
|
-
|
|
1983
|
-
this.
|
|
1022
|
+
this.state = newState
|
|
1023
|
+
this.stateHistory.push({ state: newState, timestamp: Date.now() })
|
|
1984
1024
|
return this
|
|
1985
1025
|
}
|
|
1986
1026
|
|
|
1987
|
-
|
|
1988
|
-
this.
|
|
1989
|
-
return this
|
|
1027
|
+
getStateHistory() {
|
|
1028
|
+
return this.stateHistory
|
|
1990
1029
|
}
|
|
1991
1030
|
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
}
|
|
2000
|
-
return mockResults[toolName] || { status: 'success', resultCount: 1 }
|
|
1031
|
+
startExecution(description) {
|
|
1032
|
+
const execId = crypto.randomUUID()
|
|
1033
|
+
this.currentExecution = {
|
|
1034
|
+
id: execId,
|
|
1035
|
+
description,
|
|
1036
|
+
startTime: Date.now(),
|
|
1037
|
+
steps: []
|
|
2001
1038
|
}
|
|
1039
|
+
this.state = AgentState.EXECUTING
|
|
1040
|
+
return execId
|
|
2002
1041
|
}
|
|
2003
1042
|
|
|
2004
|
-
|
|
2005
|
-
if (
|
|
2006
|
-
|
|
2007
|
-
|
|
1043
|
+
completeExecution(result, success) {
|
|
1044
|
+
if (this.currentExecution) {
|
|
1045
|
+
this.currentExecution.endTime = Date.now()
|
|
1046
|
+
this.currentExecution.result = result
|
|
1047
|
+
this.currentExecution.success = success
|
|
1048
|
+
this.executions.push(this.currentExecution)
|
|
1049
|
+
this.currentExecution = null
|
|
1050
|
+
}
|
|
1051
|
+
this.state = AgentState.READY
|
|
2008
1052
|
}
|
|
2009
1053
|
}
|
|
2010
1054
|
|
|
2011
1055
|
// ============================================================================
|
|
2012
|
-
//
|
|
1056
|
+
// MEMORY TIERS (Working, Episodic, Long-Term)
|
|
2013
1057
|
// ============================================================================
|
|
2014
1058
|
|
|
2015
|
-
// LUBM Benchmark Test Suite (12 questions)
|
|
2016
|
-
const LUBM_TEST_SUITE = [
|
|
2017
|
-
// Easy (3 tests)
|
|
2018
|
-
{
|
|
2019
|
-
index: 1,
|
|
2020
|
-
question: 'Find all professors in the university database',
|
|
2021
|
-
difficulty: 'easy',
|
|
2022
|
-
expectedPattern: 'Professor',
|
|
2023
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2024
|
-
SELECT ?x WHERE { ?x a ub:Professor }`
|
|
2025
|
-
},
|
|
2026
|
-
{
|
|
2027
|
-
index: 2,
|
|
2028
|
-
question: 'List all graduate students',
|
|
2029
|
-
difficulty: 'easy',
|
|
2030
|
-
expectedPattern: 'GraduateStudent',
|
|
2031
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2032
|
-
SELECT ?x WHERE { ?x a ub:GraduateStudent }`
|
|
2033
|
-
},
|
|
2034
|
-
{
|
|
2035
|
-
index: 3,
|
|
2036
|
-
question: 'How many courses are offered?',
|
|
2037
|
-
difficulty: 'easy',
|
|
2038
|
-
expectedPattern: 'COUNT',
|
|
2039
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2040
|
-
SELECT (COUNT(?x) AS ?count) WHERE { ?x a ub:Course }`
|
|
2041
|
-
},
|
|
2042
|
-
// Medium (5 tests)
|
|
2043
|
-
{
|
|
2044
|
-
index: 4,
|
|
2045
|
-
question: 'Find all students and their advisors',
|
|
2046
|
-
difficulty: 'medium',
|
|
2047
|
-
expectedPattern: 'advisor',
|
|
2048
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2049
|
-
SELECT ?student ?advisor WHERE { ?student ub:advisor ?advisor }`
|
|
2050
|
-
},
|
|
2051
|
-
{
|
|
2052
|
-
index: 5,
|
|
2053
|
-
question: 'List professors and the courses they teach',
|
|
2054
|
-
difficulty: 'medium',
|
|
2055
|
-
expectedPattern: 'teach',
|
|
2056
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2057
|
-
SELECT ?prof ?course WHERE { ?prof ub:teacherOf ?course }`
|
|
2058
|
-
},
|
|
2059
|
-
{
|
|
2060
|
-
index: 6,
|
|
2061
|
-
question: 'Find all departments and their parent universities',
|
|
2062
|
-
difficulty: 'medium',
|
|
2063
|
-
expectedPattern: 'subOrganization',
|
|
2064
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2065
|
-
SELECT ?dept ?univ WHERE { ?dept ub:subOrganizationOf ?univ }`
|
|
2066
|
-
},
|
|
2067
|
-
{
|
|
2068
|
-
index: 7,
|
|
2069
|
-
question: 'Count the number of students per department',
|
|
2070
|
-
difficulty: 'medium',
|
|
2071
|
-
expectedPattern: 'GROUP BY',
|
|
2072
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2073
|
-
SELECT ?dept (COUNT(?student) AS ?count) WHERE {
|
|
2074
|
-
?student ub:memberOf ?dept
|
|
2075
|
-
} GROUP BY ?dept`
|
|
2076
|
-
},
|
|
2077
|
-
{
|
|
2078
|
-
index: 8,
|
|
2079
|
-
question: 'Find the average credit hours for graduate courses',
|
|
2080
|
-
difficulty: 'medium',
|
|
2081
|
-
expectedPattern: 'AVG',
|
|
2082
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2083
|
-
SELECT (AVG(?credits) AS ?avg) WHERE {
|
|
2084
|
-
?course a ub:GraduateCourse .
|
|
2085
|
-
?course ub:creditHours ?credits
|
|
2086
|
-
}`
|
|
2087
|
-
},
|
|
2088
|
-
// Hard (4 tests)
|
|
2089
|
-
{
|
|
2090
|
-
index: 9,
|
|
2091
|
-
question: 'Find graduate students whose advisors have research interests in ML',
|
|
2092
|
-
difficulty: 'hard',
|
|
2093
|
-
expectedPattern: 'advisor',
|
|
2094
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2095
|
-
SELECT ?student WHERE {
|
|
2096
|
-
?student a ub:GraduateStudent .
|
|
2097
|
-
?student ub:advisor ?advisor .
|
|
2098
|
-
?advisor ub:researchInterest ?interest .
|
|
2099
|
-
FILTER(CONTAINS(STR(?interest), "ML"))
|
|
2100
|
-
}`
|
|
2101
|
-
},
|
|
2102
|
-
{
|
|
2103
|
-
index: 10,
|
|
2104
|
-
question: 'List publications with authors who are professors at California universities',
|
|
2105
|
-
difficulty: 'hard',
|
|
2106
|
-
expectedPattern: 'publicationAuthor',
|
|
2107
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2108
|
-
SELECT ?pub WHERE {
|
|
2109
|
-
?pub ub:publicationAuthor ?author .
|
|
2110
|
-
?author a ub:Professor .
|
|
2111
|
-
?author ub:worksFor ?dept .
|
|
2112
|
-
?dept ub:subOrganizationOf ?univ .
|
|
2113
|
-
FILTER(CONTAINS(STR(?univ), "California"))
|
|
2114
|
-
}`
|
|
2115
|
-
},
|
|
2116
|
-
{
|
|
2117
|
-
index: 11,
|
|
2118
|
-
question: 'Find students who take courses taught by professors in the same department',
|
|
2119
|
-
difficulty: 'hard',
|
|
2120
|
-
expectedPattern: 'memberOf',
|
|
2121
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2122
|
-
SELECT ?student ?course WHERE {
|
|
2123
|
-
?student ub:takesCourse ?course .
|
|
2124
|
-
?prof ub:teacherOf ?course .
|
|
2125
|
-
?student ub:memberOf ?dept .
|
|
2126
|
-
?prof ub:worksFor ?dept
|
|
2127
|
-
}`
|
|
2128
|
-
},
|
|
2129
|
-
{
|
|
2130
|
-
index: 12,
|
|
2131
|
-
question: 'Find pairs of students who share the same advisor and take common courses',
|
|
2132
|
-
difficulty: 'hard',
|
|
2133
|
-
expectedPattern: 'advisor',
|
|
2134
|
-
expectedSparql: `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2135
|
-
SELECT ?s1 ?s2 ?course WHERE {
|
|
2136
|
-
?s1 ub:advisor ?advisor .
|
|
2137
|
-
?s2 ub:advisor ?advisor .
|
|
2138
|
-
?s1 ub:takesCourse ?course .
|
|
2139
|
-
?s2 ub:takesCourse ?course .
|
|
2140
|
-
FILTER(?s1 != ?s2)
|
|
2141
|
-
}`
|
|
2142
|
-
}
|
|
2143
|
-
]
|
|
2144
|
-
|
|
2145
|
-
// Typed tool definitions for the planner
|
|
2146
|
-
const HYPERMIND_TOOLS = [
|
|
2147
|
-
{
|
|
2148
|
-
id: 'kg.sparql.query',
|
|
2149
|
-
description: 'Execute SPARQL SELECT/CONSTRUCT/ASK queries against the knowledge graph',
|
|
2150
|
-
inputType: 'String',
|
|
2151
|
-
outputType: 'BindingSet',
|
|
2152
|
-
preconditions: ['Valid SPARQL syntax'],
|
|
2153
|
-
postconditions: ['Returns solution mappings']
|
|
2154
|
-
},
|
|
2155
|
-
{
|
|
2156
|
-
id: 'kg.sparql.update',
|
|
2157
|
-
description: 'Execute SPARQL UPDATE operations (INSERT/DELETE/LOAD/CLEAR)',
|
|
2158
|
-
inputType: 'String',
|
|
2159
|
-
outputType: 'Unit',
|
|
2160
|
-
preconditions: ['Valid SPARQL Update syntax'],
|
|
2161
|
-
postconditions: ['Graph modified']
|
|
2162
|
-
},
|
|
2163
|
-
{
|
|
2164
|
-
id: 'kg.motif.find',
|
|
2165
|
-
description: 'Find graph patterns using motif DSL: (a)-[e]->(b); (b)-[]->(c)',
|
|
2166
|
-
inputType: 'String',
|
|
2167
|
-
outputType: 'MatchSet',
|
|
2168
|
-
preconditions: ['Valid motif pattern'],
|
|
2169
|
-
postconditions: ['Returns matched subgraphs']
|
|
2170
|
-
},
|
|
2171
|
-
{
|
|
2172
|
-
id: 'kg.datalog.apply',
|
|
2173
|
-
description: 'Apply Datalog rules for reasoning and inference',
|
|
2174
|
-
inputType: 'Program',
|
|
2175
|
-
outputType: 'FactSet',
|
|
2176
|
-
preconditions: ['Valid Datalog program'],
|
|
2177
|
-
postconditions: ['Returns derived facts']
|
|
2178
|
-
},
|
|
2179
|
-
{
|
|
2180
|
-
id: 'kg.semantic.search',
|
|
2181
|
-
description: 'Find semantically similar entities using vector embeddings',
|
|
2182
|
-
inputType: 'Record(entity: Node, k: Int, threshold: Float)',
|
|
2183
|
-
outputType: 'List(Record(entity: Node, similarity: Float))',
|
|
2184
|
-
preconditions: ['Entity has embedding', 'k > 0', 'threshold in [0, 1]'],
|
|
2185
|
-
postconditions: ['Returns top-k similar entities']
|
|
2186
|
-
},
|
|
2187
|
-
{
|
|
2188
|
-
id: 'kg.traversal.oneHop',
|
|
2189
|
-
description: 'Get immediate neighbors of an entity',
|
|
2190
|
-
inputType: 'Node',
|
|
2191
|
-
outputType: 'List(Node)',
|
|
2192
|
-
preconditions: ['Entity exists in graph'],
|
|
2193
|
-
postconditions: ['Returns adjacent nodes']
|
|
2194
|
-
},
|
|
2195
|
-
{
|
|
2196
|
-
id: 'kg.traversal.paths',
|
|
2197
|
-
description: 'Find all paths between two entities up to max length',
|
|
2198
|
-
inputType: 'Record(source: Node, target: Node, maxLength: Int)',
|
|
2199
|
-
outputType: 'List(Path)',
|
|
2200
|
-
preconditions: ['Entities exist', 'maxLength > 0'],
|
|
2201
|
-
postconditions: ['Returns discovered paths']
|
|
2202
|
-
}
|
|
2203
|
-
]
|
|
2204
|
-
|
|
2205
|
-
// Default LUBM ontology hints
|
|
2206
|
-
const LUBM_HINTS = [
|
|
2207
|
-
'Database uses LUBM (Lehigh University Benchmark) ontology',
|
|
2208
|
-
'Namespace: http://swat.cse.lehigh.edu/onto/univ-bench.owl#',
|
|
2209
|
-
'Key classes: University, Department, Professor, AssociateProfessor, AssistantProfessor, Lecturer, GraduateStudent, UndergraduateStudent, Course, GraduateCourse, Publication, Research',
|
|
2210
|
-
'Key properties: worksFor, memberOf, advisor, takesCourse, teacherOf, publicationAuthor, subOrganizationOf, researchInterest, name, emailAddress, telephone',
|
|
2211
|
-
'Professor subtypes: AssociateProfessor, AssistantProfessor, FullProfessor'
|
|
2212
|
-
]
|
|
2213
|
-
|
|
2214
1059
|
/**
|
|
2215
|
-
*
|
|
1060
|
+
* WorkingMemory - Fast, ephemeral context (like CPU registers)
|
|
2216
1061
|
*/
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
const reqOptions = {
|
|
2224
|
-
hostname: urlObj.hostname,
|
|
2225
|
-
port: urlObj.port || (isHttps ? 443 : 80),
|
|
2226
|
-
path: urlObj.pathname + urlObj.search,
|
|
2227
|
-
method: options.method || 'GET',
|
|
2228
|
-
headers: options.headers || {},
|
|
2229
|
-
timeout: options.timeout || 30000
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
const req = client.request(reqOptions, res => {
|
|
2233
|
-
let data = ''
|
|
2234
|
-
res.on('data', chunk => (data += chunk))
|
|
2235
|
-
res.on('end', () => {
|
|
2236
|
-
resolve({
|
|
2237
|
-
status: res.statusCode,
|
|
2238
|
-
headers: res.headers,
|
|
2239
|
-
data: data
|
|
2240
|
-
})
|
|
2241
|
-
})
|
|
2242
|
-
})
|
|
2243
|
-
|
|
2244
|
-
req.on('error', reject)
|
|
2245
|
-
req.on('timeout', () => {
|
|
2246
|
-
req.destroy()
|
|
2247
|
-
reject(new Error('Request timeout'))
|
|
2248
|
-
})
|
|
1062
|
+
class WorkingMemory {
|
|
1063
|
+
constructor(capacity = 100) {
|
|
1064
|
+
this.capacity = capacity
|
|
1065
|
+
this.items = new Map()
|
|
1066
|
+
this.accessOrder = []
|
|
1067
|
+
}
|
|
2249
1068
|
|
|
2250
|
-
|
|
2251
|
-
|
|
1069
|
+
set(key, value) {
|
|
1070
|
+
if (this.items.size >= this.capacity && !this.items.has(key)) {
|
|
1071
|
+
// LRU eviction
|
|
1072
|
+
const oldest = this.accessOrder.shift()
|
|
1073
|
+
this.items.delete(oldest)
|
|
2252
1074
|
}
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
/**
|
|
2258
|
-
* Validate SPARQL syntax (basic validation)
|
|
2259
|
-
*/
|
|
2260
|
-
function validateSparqlSyntax(sparql) {
|
|
2261
|
-
if (!sparql || typeof sparql !== 'string') return false
|
|
2262
|
-
|
|
2263
|
-
// Remove markdown code blocks if present
|
|
2264
|
-
let cleaned = sparql.trim()
|
|
2265
|
-
if (cleaned.startsWith('```')) {
|
|
2266
|
-
cleaned = cleaned.replace(/^```\w*\n?/, '').replace(/\n?```$/, '')
|
|
1075
|
+
this.items.set(key, { value, timestamp: Date.now() })
|
|
1076
|
+
this._updateAccess(key)
|
|
2267
1077
|
}
|
|
2268
1078
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
const hasQueryForm = hasSelect || hasConstruct || hasAsk || hasDescribe
|
|
1079
|
+
get(key) {
|
|
1080
|
+
const item = this.items.get(key)
|
|
1081
|
+
if (item) {
|
|
1082
|
+
this._updateAccess(key)
|
|
1083
|
+
return item.value
|
|
1084
|
+
}
|
|
1085
|
+
return null
|
|
1086
|
+
}
|
|
2278
1087
|
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
1088
|
+
_updateAccess(key) {
|
|
1089
|
+
const idx = this.accessOrder.indexOf(key)
|
|
1090
|
+
if (idx > -1) this.accessOrder.splice(idx, 1)
|
|
1091
|
+
this.accessOrder.push(key)
|
|
1092
|
+
}
|
|
2283
1093
|
|
|
2284
|
-
|
|
2285
|
-
|
|
1094
|
+
clear() {
|
|
1095
|
+
this.items.clear()
|
|
1096
|
+
this.accessOrder = []
|
|
1097
|
+
}
|
|
2286
1098
|
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
*/
|
|
2290
|
-
function createPlanningContext(endpoint, hints = []) {
|
|
2291
|
-
return {
|
|
2292
|
-
tools: HYPERMIND_TOOLS,
|
|
2293
|
-
hints: [...LUBM_HINTS, ...hints],
|
|
2294
|
-
endpoint: endpoint
|
|
1099
|
+
size() {
|
|
1100
|
+
return this.items.size
|
|
2295
1101
|
}
|
|
2296
|
-
}
|
|
2297
1102
|
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
function getHyperMindBenchmarkSuite() {
|
|
2302
|
-
return LUBM_TEST_SUITE.map(t => ({
|
|
2303
|
-
index: t.index,
|
|
2304
|
-
question: t.question,
|
|
2305
|
-
difficulty: t.difficulty,
|
|
2306
|
-
expectedPattern: t.expectedPattern
|
|
2307
|
-
}))
|
|
1103
|
+
toJSON() {
|
|
1104
|
+
return Object.fromEntries(this.items)
|
|
1105
|
+
}
|
|
2308
1106
|
}
|
|
2309
1107
|
|
|
2310
1108
|
/**
|
|
2311
|
-
*
|
|
1109
|
+
* EpisodicMemory - Execution history with temporal ordering
|
|
2312
1110
|
*/
|
|
2313
|
-
class
|
|
2314
|
-
constructor(
|
|
2315
|
-
this.
|
|
2316
|
-
this.
|
|
2317
|
-
this.tools = spec.tools || ['kg.sparql.query']
|
|
2318
|
-
this.endpoint = spec.endpoint || 'http://rust-kgdb-coordinator:30080'
|
|
2319
|
-
this.timeout = spec.timeout || 30000
|
|
2320
|
-
this.tracing = spec.tracing || false
|
|
2321
|
-
this.trace = []
|
|
2322
|
-
this.context = createPlanningContext(this.endpoint)
|
|
1111
|
+
class EpisodicMemory {
|
|
1112
|
+
constructor(limit = 1000) {
|
|
1113
|
+
this.limit = limit
|
|
1114
|
+
this.episodes = []
|
|
2323
1115
|
}
|
|
2324
1116
|
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (!connected) {
|
|
2335
|
-
console.warn(`Warning: Could not connect to ${spec.endpoint}`)
|
|
2336
|
-
}
|
|
2337
|
-
} catch (err) {
|
|
2338
|
-
console.warn(`Warning: Connection check failed: ${err.message}`)
|
|
1117
|
+
store(episode) {
|
|
1118
|
+
this.episodes.push({
|
|
1119
|
+
...episode,
|
|
1120
|
+
id: crypto.randomUUID(),
|
|
1121
|
+
timestamp: new Date().toISOString(),
|
|
1122
|
+
accessCount: 0
|
|
1123
|
+
})
|
|
1124
|
+
if (this.episodes.length > this.limit) {
|
|
1125
|
+
this.episodes.shift()
|
|
2339
1126
|
}
|
|
2340
|
-
|
|
2341
|
-
return agent
|
|
2342
1127
|
}
|
|
2343
1128
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
if (this.model === 'mock') {
|
|
2358
|
-
sparql = this._generateMockSparql(prompt)
|
|
2359
|
-
rawSparql = sparql // Mock always produces clean output
|
|
2360
|
-
rawIsValid = true
|
|
2361
|
-
} else {
|
|
2362
|
-
// Call LLM API - returns { raw, cleaned, rawIsValid }
|
|
2363
|
-
const llmResponse = await this._callLlmForSparql(prompt)
|
|
2364
|
-
this._lastLlmResponse = llmResponse
|
|
2365
|
-
rawSparql = llmResponse.raw
|
|
2366
|
-
rawIsValid = llmResponse.rawIsValid
|
|
2367
|
-
sparql = llmResponse.cleaned // HyperMind uses cleaned version
|
|
2368
|
-
}
|
|
1129
|
+
retrieve(query, limit = 10) {
|
|
1130
|
+
// Simple relevance: count matching words
|
|
1131
|
+
const queryWords = new Set(query.toLowerCase().split(/\s+/))
|
|
1132
|
+
return this.episodes
|
|
1133
|
+
.map(ep => {
|
|
1134
|
+
const promptWords = new Set((ep.prompt || '').toLowerCase().split(/\s+/))
|
|
1135
|
+
const overlap = [...queryWords].filter(w => promptWords.has(w)).length
|
|
1136
|
+
return { episode: ep, relevance: overlap / Math.max(queryWords.size, 1) }
|
|
1137
|
+
})
|
|
1138
|
+
.sort((a, b) => b.relevance - a.relevance)
|
|
1139
|
+
.slice(0, limit)
|
|
1140
|
+
}
|
|
2369
1141
|
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
}
|
|
1142
|
+
getRecent(n = 10) {
|
|
1143
|
+
return this.episodes.slice(-n)
|
|
1144
|
+
}
|
|
2374
1145
|
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
if (this.tracing) {
|
|
2380
|
-
this.trace.push({
|
|
2381
|
-
timestamp: new Date().toISOString(),
|
|
2382
|
-
tool: 'kg.sparql.query',
|
|
2383
|
-
input: prompt,
|
|
2384
|
-
output: JSON.stringify(results),
|
|
2385
|
-
durationMs: Date.now() - startTime,
|
|
2386
|
-
success: true,
|
|
2387
|
-
rawIsValid: rawIsValid
|
|
2388
|
-
})
|
|
2389
|
-
}
|
|
1146
|
+
size() {
|
|
1147
|
+
return this.episodes.length
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
2390
1150
|
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
if (this.tracing) {
|
|
2400
|
-
this.trace.push({
|
|
2401
|
-
timestamp: new Date().toISOString(),
|
|
2402
|
-
tool: 'kg.sparql.query',
|
|
2403
|
-
input: prompt,
|
|
2404
|
-
output: error.message,
|
|
2405
|
-
durationMs: Date.now() - startTime,
|
|
2406
|
-
success: false
|
|
2407
|
-
})
|
|
2408
|
-
}
|
|
1151
|
+
/**
|
|
1152
|
+
* LongTermMemory - Persistent knowledge graph storage
|
|
1153
|
+
*/
|
|
1154
|
+
class LongTermMemory {
|
|
1155
|
+
constructor(kg) {
|
|
1156
|
+
this.kg = kg
|
|
1157
|
+
this.memoryGraph = 'http://memory.hypermind.ai/'
|
|
1158
|
+
}
|
|
2409
1159
|
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
1160
|
+
store(subject, predicate, object) {
|
|
1161
|
+
if (this.kg) {
|
|
1162
|
+
try {
|
|
1163
|
+
this.kg.insertTriple(subject, predicate, object, this.memoryGraph)
|
|
1164
|
+
return true
|
|
1165
|
+
} catch (e) {
|
|
1166
|
+
return false
|
|
2416
1167
|
}
|
|
2417
1168
|
}
|
|
1169
|
+
return false
|
|
2418
1170
|
}
|
|
2419
1171
|
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
// Match against test suite
|
|
2427
|
-
for (const test of LUBM_TEST_SUITE) {
|
|
2428
|
-
if (lowerPrompt.includes(test.question.toLowerCase().slice(0, 20))) {
|
|
2429
|
-
return test.expectedSparql
|
|
1172
|
+
query(sparql) {
|
|
1173
|
+
if (this.kg) {
|
|
1174
|
+
try {
|
|
1175
|
+
return this.kg.querySelect(sparql)
|
|
1176
|
+
} catch (e) {
|
|
1177
|
+
return []
|
|
2430
1178
|
}
|
|
2431
1179
|
}
|
|
2432
|
-
|
|
2433
|
-
// Default SPARQL
|
|
2434
|
-
return `PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2435
|
-
SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10`
|
|
1180
|
+
return []
|
|
2436
1181
|
}
|
|
2437
1182
|
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
IMPORTANT RULES:
|
|
2447
|
-
1. ONLY output a valid SPARQL query - no explanations, no markdown, no backticks
|
|
2448
|
-
2. Use the LUBM ontology prefix: PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2449
|
-
3. Common LUBM classes: Professor, GraduateStudent, UndergraduateStudent, Course, Department, University
|
|
2450
|
-
4. Common LUBM properties: name, advisor, teacherOf, takesCourse, memberOf, subOrganizationOf, worksFor, researchInterest, publicationAuthor
|
|
2451
|
-
|
|
2452
|
-
EXAMPLES:
|
|
2453
|
-
Q: "Find all professors"
|
|
2454
|
-
A: PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2455
|
-
SELECT ?x WHERE { ?x a ub:Professor }
|
|
2456
|
-
|
|
2457
|
-
Q: "How many courses are there?"
|
|
2458
|
-
A: PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2459
|
-
SELECT (COUNT(?x) AS ?count) WHERE { ?x a ub:Course }
|
|
2460
|
-
|
|
2461
|
-
Q: "Find students and their advisors"
|
|
2462
|
-
A: PREFIX ub: <http://swat.cse.lehigh.edu/onto/univ-bench.owl#>
|
|
2463
|
-
SELECT ?student ?advisor WHERE { ?student ub:advisor ?advisor }
|
|
2464
|
-
|
|
2465
|
-
Now generate a SPARQL query for the following question. Output ONLY the SPARQL query, nothing else:`
|
|
2466
|
-
|
|
2467
|
-
if (this.model.includes('claude') || this.model.includes('anthropic')) {
|
|
2468
|
-
return this._callAnthropic(systemPrompt, prompt)
|
|
2469
|
-
} else if (this.model.includes('gpt') || this.model.includes('openai')) {
|
|
2470
|
-
return this._callOpenAI(systemPrompt, prompt)
|
|
2471
|
-
} else {
|
|
2472
|
-
throw new Error(`Unknown model: ${this.model}. Supported: claude-sonnet-4, gpt-4o, mock`)
|
|
2473
|
-
}
|
|
1183
|
+
getRelated(entity, limit = 10) {
|
|
1184
|
+
const sparql = `
|
|
1185
|
+
SELECT ?p ?o WHERE {
|
|
1186
|
+
<${entity}> ?p ?o .
|
|
1187
|
+
} LIMIT ${limit}
|
|
1188
|
+
`
|
|
1189
|
+
return this.query(sparql)
|
|
2474
1190
|
}
|
|
1191
|
+
}
|
|
2475
1192
|
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
_lastLlmResponse = null
|
|
1193
|
+
// ============================================================================
|
|
1194
|
+
// GOVERNANCE LAYER
|
|
1195
|
+
// ============================================================================
|
|
2480
1196
|
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
1197
|
+
/**
|
|
1198
|
+
* GovernancePolicy - Defines capabilities and limits for agent execution
|
|
1199
|
+
*/
|
|
1200
|
+
class GovernancePolicy {
|
|
1201
|
+
constructor(config = {}) {
|
|
1202
|
+
this.capabilities = new Set(config.capabilities || ['ReadKG', 'ExecuteTool'])
|
|
1203
|
+
this.limits = {
|
|
1204
|
+
maxMemoryMB: config.maxMemoryMB || 256,
|
|
1205
|
+
maxExecutionTimeMs: config.maxExecutionTimeMs || 60000,
|
|
1206
|
+
maxToolCalls: config.maxToolCalls || 100
|
|
2489
1207
|
}
|
|
1208
|
+
this.auditEnabled = config.auditEnabled !== false
|
|
1209
|
+
}
|
|
2490
1210
|
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
model: modelId,
|
|
2495
|
-
max_tokens: 1024,
|
|
2496
|
-
system: systemPrompt,
|
|
2497
|
-
messages: [{ role: 'user', content: userPrompt }]
|
|
2498
|
-
})
|
|
2499
|
-
|
|
2500
|
-
const response = await httpRequest('https://api.anthropic.com/v1/messages', {
|
|
2501
|
-
method: 'POST',
|
|
2502
|
-
headers: {
|
|
2503
|
-
'Content-Type': 'application/json',
|
|
2504
|
-
'x-api-key': apiKey,
|
|
2505
|
-
'anthropic-version': '2023-06-01'
|
|
2506
|
-
},
|
|
2507
|
-
body: requestBody,
|
|
2508
|
-
timeout: 30000
|
|
2509
|
-
})
|
|
1211
|
+
hasCapability(cap) {
|
|
1212
|
+
return this.capabilities.has(cap)
|
|
1213
|
+
}
|
|
2510
1214
|
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
1215
|
+
addCapability(cap) {
|
|
1216
|
+
this.capabilities.add(cap)
|
|
1217
|
+
}
|
|
2514
1218
|
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
1219
|
+
removeCapability(cap) {
|
|
1220
|
+
this.capabilities.delete(cap)
|
|
1221
|
+
}
|
|
2518
1222
|
|
|
2519
|
-
|
|
1223
|
+
checkLimits(usage) {
|
|
2520
1224
|
return {
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
1225
|
+
memoryOk: (usage.memoryMB || 0) <= this.limits.maxMemoryMB,
|
|
1226
|
+
timeOk: (usage.executionTimeMs || 0) <= this.limits.maxExecutionTimeMs,
|
|
1227
|
+
toolCallsOk: (usage.toolCalls || 0) <= this.limits.maxToolCalls
|
|
2524
1228
|
}
|
|
2525
1229
|
}
|
|
1230
|
+
}
|
|
2526
1231
|
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
const modelId = this.model === 'gpt-4o' ? 'gpt-4o' : this.model
|
|
2538
|
-
|
|
2539
|
-
const requestBody = JSON.stringify({
|
|
2540
|
-
model: modelId,
|
|
2541
|
-
messages: [
|
|
2542
|
-
{ role: 'system', content: systemPrompt },
|
|
2543
|
-
{ role: 'user', content: userPrompt }
|
|
2544
|
-
],
|
|
2545
|
-
max_tokens: 1024,
|
|
2546
|
-
temperature: 0.1
|
|
2547
|
-
})
|
|
1232
|
+
/**
|
|
1233
|
+
* GovernanceEngine - Enforces policies and maintains audit trail
|
|
1234
|
+
*/
|
|
1235
|
+
class GovernanceEngine {
|
|
1236
|
+
constructor(policy) {
|
|
1237
|
+
this.policy = policy
|
|
1238
|
+
this.auditLog = []
|
|
1239
|
+
this.usage = { memoryMB: 0, executionTimeMs: 0, toolCalls: 0 }
|
|
1240
|
+
}
|
|
2548
1241
|
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
'Content-Type': 'application/json',
|
|
2553
|
-
'Authorization': `Bearer ${apiKey}`
|
|
2554
|
-
},
|
|
2555
|
-
body: requestBody,
|
|
2556
|
-
timeout: 30000
|
|
2557
|
-
})
|
|
1242
|
+
authorize(action, args) {
|
|
1243
|
+
const requiredCap = this._actionToCapability(action)
|
|
1244
|
+
const authorized = this.policy.hasCapability(requiredCap)
|
|
2558
1245
|
|
|
2559
|
-
if (
|
|
2560
|
-
|
|
1246
|
+
if (this.policy.auditEnabled) {
|
|
1247
|
+
this.auditLog.push({
|
|
1248
|
+
timestamp: new Date().toISOString(),
|
|
1249
|
+
action,
|
|
1250
|
+
args: JSON.stringify(args).slice(0, 200),
|
|
1251
|
+
authorized,
|
|
1252
|
+
capability: requiredCap
|
|
1253
|
+
})
|
|
2561
1254
|
}
|
|
2562
1255
|
|
|
2563
|
-
|
|
2564
|
-
const rawText = data.choices[0].message.content.trim()
|
|
2565
|
-
const cleanedText = this._cleanSparqlResponse(rawText)
|
|
2566
|
-
|
|
2567
|
-
// Return both raw and cleaned for comparison benchmarking
|
|
2568
|
-
return {
|
|
2569
|
-
raw: rawText,
|
|
2570
|
-
cleaned: cleanedText,
|
|
2571
|
-
rawIsValid: validateSparqlSyntax(rawText)
|
|
2572
|
-
}
|
|
1256
|
+
return { authorized, reason: authorized ? 'OK' : `Missing capability: ${requiredCap}` }
|
|
2573
1257
|
}
|
|
2574
1258
|
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
// Remove markdown code blocks
|
|
2580
|
-
let clean = text.replace(/```sparql\n?/gi, '').replace(/```sql\n?/gi, '').replace(/```\n?/g, '')
|
|
2581
|
-
// Remove leading/trailing whitespace
|
|
2582
|
-
clean = clean.trim()
|
|
2583
|
-
// If it starts with "SPARQL:" or similar, remove it
|
|
2584
|
-
clean = clean.replace(/^sparql:\s*/i, '')
|
|
2585
|
-
return clean
|
|
1259
|
+
recordUsage(type, amount) {
|
|
1260
|
+
if (type === 'toolCall') this.usage.toolCalls++
|
|
1261
|
+
if (type === 'memory') this.usage.memoryMB = amount
|
|
1262
|
+
if (type === 'time') this.usage.executionTimeMs = amount
|
|
2586
1263
|
}
|
|
2587
1264
|
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
async _executeSparql(sparql) {
|
|
2592
|
-
const url = `${this.endpoint}/sparql`
|
|
2593
|
-
|
|
2594
|
-
const response = await httpRequest(url, {
|
|
2595
|
-
method: 'POST',
|
|
2596
|
-
headers: {
|
|
2597
|
-
'Content-Type': 'application/sparql-query',
|
|
2598
|
-
Accept: 'application/json'
|
|
2599
|
-
},
|
|
2600
|
-
body: sparql,
|
|
2601
|
-
timeout: this.timeout
|
|
2602
|
-
})
|
|
1265
|
+
checkLimits() {
|
|
1266
|
+
return this.policy.checkLimits(this.usage)
|
|
1267
|
+
}
|
|
2603
1268
|
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
1269
|
+
getAuditLog() {
|
|
1270
|
+
return this.auditLog
|
|
1271
|
+
}
|
|
2607
1272
|
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
return data.results.map(row => ({
|
|
2616
|
-
bindings: Object.fromEntries(
|
|
2617
|
-
Object.entries(row).map(([k, v]) => {
|
|
2618
|
-
// Strip leading ? from variable names and typed literal suffix
|
|
2619
|
-
const cleanKey = k.startsWith('?') ? k.slice(1) : k
|
|
2620
|
-
let cleanVal = v
|
|
2621
|
-
// Handle typed literals: "11"^^<http://www.w3.org/2001/XMLSchema#integer>
|
|
2622
|
-
if (typeof v === 'string' && v.includes('^^<')) {
|
|
2623
|
-
cleanVal = v.split('^^')[0].replace(/^"|"$/g, '')
|
|
2624
|
-
}
|
|
2625
|
-
return [cleanKey, cleanVal]
|
|
2626
|
-
})
|
|
2627
|
-
)
|
|
2628
|
-
}))
|
|
2629
|
-
} else if (data.results && data.results.bindings) {
|
|
2630
|
-
// W3C SPARQL JSON format
|
|
2631
|
-
return data.results.bindings.map(b => ({
|
|
2632
|
-
bindings: Object.fromEntries(
|
|
2633
|
-
Object.entries(b).map(([k, v]) => [k, v.value])
|
|
2634
|
-
)
|
|
2635
|
-
}))
|
|
2636
|
-
}
|
|
2637
|
-
return data
|
|
2638
|
-
} catch (e) {
|
|
2639
|
-
throw new Error(`Failed to parse SPARQL results: ${e.message}`)
|
|
1273
|
+
_actionToCapability(action) {
|
|
1274
|
+
const mapping = {
|
|
1275
|
+
'query': 'ReadKG',
|
|
1276
|
+
'insert': 'WriteKG',
|
|
1277
|
+
'delete': 'WriteKG',
|
|
1278
|
+
'execute_tool': 'ExecuteTool',
|
|
1279
|
+
'use_embeddings': 'UseEmbeddings'
|
|
2640
1280
|
}
|
|
1281
|
+
return mapping[action] || 'ExecuteTool'
|
|
2641
1282
|
}
|
|
1283
|
+
}
|
|
2642
1284
|
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
1285
|
+
// ============================================================================
|
|
1286
|
+
// AGENT SCOPE
|
|
1287
|
+
// ============================================================================
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* AgentScope - Namespace isolation for multi-tenant execution
|
|
1291
|
+
*/
|
|
1292
|
+
class AgentScope {
|
|
1293
|
+
constructor(config = {}) {
|
|
1294
|
+
this.name = config.name || 'default-scope'
|
|
1295
|
+
this.namespace = {
|
|
1296
|
+
graphUri: config.graphUri || 'http://default.scope/',
|
|
1297
|
+
allowedGraphs: config.allowedGraphs || [config.graphUri || 'http://default.scope/'],
|
|
1298
|
+
prefix: config.prefix || 'scope'
|
|
1299
|
+
}
|
|
1300
|
+
this.limits = {
|
|
1301
|
+
maxToolCalls: config.maxToolCalls || 50,
|
|
1302
|
+
maxResults: config.maxResults || 1000,
|
|
1303
|
+
maxGraphQueries: config.maxGraphQueries || 100
|
|
1304
|
+
}
|
|
1305
|
+
this.toolCalls = 0
|
|
1306
|
+
this.usage = {
|
|
1307
|
+
toolCalls: 0,
|
|
1308
|
+
graphQueries: 0,
|
|
1309
|
+
bytesProcessed: 0
|
|
1310
|
+
}
|
|
2648
1311
|
}
|
|
2649
1312
|
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
*/
|
|
2653
|
-
getContext() {
|
|
2654
|
-
return this.context
|
|
1313
|
+
isGraphAllowed(graphUri) {
|
|
1314
|
+
return this.namespace.allowedGraphs.some(g => graphUri.startsWith(g))
|
|
2655
1315
|
}
|
|
2656
1316
|
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
const response = await httpRequest(`${this.endpoint}/health`, {
|
|
2663
|
-
timeout: 5000
|
|
2664
|
-
})
|
|
2665
|
-
return response.status === 200
|
|
2666
|
-
} catch {
|
|
2667
|
-
return false
|
|
1317
|
+
recordToolCall() {
|
|
1318
|
+
this.toolCalls++
|
|
1319
|
+
this.usage.toolCalls++
|
|
1320
|
+
if (this.toolCalls > this.limits.maxToolCalls) {
|
|
1321
|
+
throw new Error(`Scope "${this.name}" exceeded tool call limit (${this.limits.maxToolCalls})`)
|
|
2668
1322
|
}
|
|
2669
1323
|
}
|
|
2670
1324
|
|
|
2671
|
-
|
|
2672
|
-
return this.
|
|
1325
|
+
getScopedUri(localName) {
|
|
1326
|
+
return `${this.namespace.graphUri}${this.namespace.prefix}:${localName}`
|
|
2673
1327
|
}
|
|
2674
1328
|
|
|
2675
|
-
|
|
2676
|
-
return
|
|
1329
|
+
getUsage() {
|
|
1330
|
+
return {
|
|
1331
|
+
name: this.name,
|
|
1332
|
+
toolCalls: this.toolCalls,
|
|
1333
|
+
maxToolCalls: this.limits.maxToolCalls,
|
|
1334
|
+
...this.usage
|
|
1335
|
+
}
|
|
2677
1336
|
}
|
|
2678
1337
|
|
|
2679
|
-
// ==========================================================================
|
|
2680
|
-
// MEMORY HYPERGRAPH APIs - Typed interface for Memory + KG operations
|
|
2681
|
-
// ==========================================================================
|
|
2682
|
-
|
|
2683
1338
|
/**
|
|
2684
|
-
*
|
|
2685
|
-
*
|
|
2686
|
-
*
|
|
2687
|
-
* @param {Object} options - Recall options
|
|
2688
|
-
* @param {string} options.query - Natural language query (e.g., "Provider P001 fraud")
|
|
2689
|
-
* @param {Object} options.kgFilter - Optional KG filter {predicate, operator, value}
|
|
2690
|
-
* @param {number} options.limit - Max results (default 10)
|
|
2691
|
-
* @returns {Promise<Object>} Enriched results with episode, finding, kgContext, semanticHash
|
|
2692
|
-
*
|
|
2693
|
-
* @example
|
|
2694
|
-
* const results = await agent.recallWithKG({
|
|
2695
|
-
* query: "Provider P001 fraud",
|
|
2696
|
-
* kgFilter: { predicate: ":amount", operator: ">", value: 25000 },
|
|
2697
|
-
* limit: 10
|
|
2698
|
-
* })
|
|
1339
|
+
* Track resource usage
|
|
1340
|
+
* @param {string} resource - Resource type (toolCalls, graphQueries, bytesProcessed)
|
|
1341
|
+
* @param {number} amount - Amount to add to usage
|
|
2699
1342
|
*/
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
// Check semantic cache
|
|
2707
|
-
if (this._semanticCache && this._semanticCache.has(semanticHash)) {
|
|
2708
|
-
return {
|
|
2709
|
-
results: this._semanticCache.get(semanticHash),
|
|
2710
|
-
fromCache: true,
|
|
2711
|
-
semanticHash
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
// Build and execute memory + KG SPARQL
|
|
2716
|
-
const sparql = this._buildMemoryKGSparql(query, kgFilter, limit)
|
|
2717
|
-
|
|
2718
|
-
try {
|
|
2719
|
-
const rawResults = await this._executeSparql(sparql)
|
|
2720
|
-
|
|
2721
|
-
const enrichedResults = rawResults.map(r => ({
|
|
2722
|
-
episode: r.episode || 'Episode:unknown',
|
|
2723
|
-
finding: r.finding || query,
|
|
2724
|
-
kgContext: r.kgEntity ? { entity: r.kgEntity, value: r.kgValue } : null,
|
|
2725
|
-
semanticHash
|
|
2726
|
-
}))
|
|
2727
|
-
|
|
2728
|
-
// Cache results
|
|
2729
|
-
if (!this._semanticCache) this._semanticCache = new Map()
|
|
2730
|
-
this._semanticCache.set(semanticHash, enrichedResults)
|
|
2731
|
-
|
|
2732
|
-
return { results: enrichedResults, fromCache: false, semanticHash }
|
|
2733
|
-
} catch (err) {
|
|
2734
|
-
// Fallback to basic recall if KG query fails
|
|
2735
|
-
return { results: [], error: err.message, semanticHash }
|
|
1343
|
+
trackUsage(resource, amount = 1) {
|
|
1344
|
+
if (this.usage.hasOwnProperty(resource)) {
|
|
1345
|
+
this.usage[resource] += amount
|
|
1346
|
+
} else {
|
|
1347
|
+
this.usage[resource] = amount
|
|
2736
1348
|
}
|
|
2737
1349
|
}
|
|
2738
1350
|
|
|
2739
1351
|
/**
|
|
2740
|
-
*
|
|
2741
|
-
* Research: SimHash (Charikar, 2002), Semantic Hashing (Salakhutdinov & Hinton, 2009)
|
|
1352
|
+
* Get remaining resources before limits are exceeded
|
|
2742
1353
|
*/
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
for (const match of text.matchAll(entityPattern)) {
|
|
2749
|
-
parts.push(`${match[1].toLowerCase()}-${match[2].toLowerCase()}`)
|
|
2750
|
-
}
|
|
2751
|
-
|
|
2752
|
-
// Extract action keywords
|
|
2753
|
-
const actions = ['fraud', 'detect', 'analyze', 'claim', 'risk', 'pattern', 'deny', 'approve']
|
|
2754
|
-
const tokens = text.toLowerCase().split(/\s+/)
|
|
2755
|
-
for (const token of tokens) {
|
|
2756
|
-
if (actions.some(a => token.includes(a))) {
|
|
2757
|
-
parts.push(token)
|
|
2758
|
-
}
|
|
1354
|
+
getRemainingResources() {
|
|
1355
|
+
return {
|
|
1356
|
+
toolCalls: this.limits.maxToolCalls - this.usage.toolCalls,
|
|
1357
|
+
graphQueries: this.limits.maxGraphQueries - (this.usage.graphQueries || 0),
|
|
1358
|
+
results: this.limits.maxResults
|
|
2759
1359
|
}
|
|
2760
|
-
|
|
2761
|
-
return `semhash:${parts.slice(0, 5).join('-') || 'general'}`
|
|
2762
1360
|
}
|
|
2763
1361
|
|
|
2764
1362
|
/**
|
|
2765
|
-
*
|
|
1363
|
+
* Check if resource limit has been exceeded
|
|
1364
|
+
* @param {string} resource - Resource type to check
|
|
2766
1365
|
*/
|
|
2767
|
-
|
|
2768
|
-
const
|
|
2769
|
-
|
|
2770
|
-
: ''
|
|
2771
|
-
|
|
2772
|
-
// Extract potential entity URIs from query
|
|
2773
|
-
const entityPattern = /([A-Z][a-z]+)[:\s]?([A-Z0-9]+)/g
|
|
2774
|
-
const entities = []
|
|
2775
|
-
for (const match of query.matchAll(entityPattern)) {
|
|
2776
|
-
entities.push(`<http://example.org/${match[1]}/${match[2]}>`)
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
const valuesClause = entities.length > 0
|
|
2780
|
-
? `VALUES ?entity { ${entities.join(' ')} }`
|
|
2781
|
-
: '?entity a <http://www.w3.org/2000/01/rdf-schema#Resource>'
|
|
2782
|
-
|
|
2783
|
-
return `
|
|
2784
|
-
PREFIX am: <https://gonnect.ai/ontology/agent-memory#>
|
|
2785
|
-
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
2786
|
-
|
|
2787
|
-
SELECT ?episode ?finding ?kgEntity ?kgValue WHERE {
|
|
2788
|
-
OPTIONAL {
|
|
2789
|
-
GRAPH <https://gonnect.ai/memory/> {
|
|
2790
|
-
?episode a am:Episode ; am:prompt ?finding .
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
OPTIONAL {
|
|
2794
|
-
${valuesClause}
|
|
2795
|
-
?entity ?pred ?kgValue .
|
|
2796
|
-
BIND(?entity AS ?kgEntity)
|
|
2797
|
-
${filterClause}
|
|
2798
|
-
}
|
|
2799
|
-
} LIMIT ${limit}`
|
|
1366
|
+
isLimitExceeded(resource) {
|
|
1367
|
+
const remaining = this.getRemainingResources()
|
|
1368
|
+
return remaining[resource] !== undefined && remaining[resource] <= 0
|
|
2800
1369
|
}
|
|
2801
1370
|
}
|
|
2802
1371
|
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
* KEY COMPARISON:
|
|
2807
|
-
* - "Vanilla LLM" = Raw LLM output WITHOUT HyperMind cleaning
|
|
2808
|
-
* - "HyperMind Agent" = LLM output WITH typed tools, cleaning, validation
|
|
2809
|
-
*
|
|
2810
|
-
* This shows the TRUE value of HyperMind by comparing:
|
|
2811
|
-
* 1. How often raw LLM output has syntax issues (markdown, backticks, etc)
|
|
2812
|
-
* 2. How HyperMind fixes these issues with _cleanSparqlResponse()
|
|
2813
|
-
*/
|
|
2814
|
-
async function runHyperMindBenchmark(endpoint, model, options = {}) {
|
|
2815
|
-
const testSuite = options.testIndices
|
|
2816
|
-
? LUBM_TEST_SUITE.filter(t => options.testIndices.includes(t.index))
|
|
2817
|
-
: LUBM_TEST_SUITE
|
|
2818
|
-
|
|
2819
|
-
const results = []
|
|
2820
|
-
let rawSyntaxSuccess = 0 // Vanilla LLM: raw output passes validation
|
|
2821
|
-
let hypermindSyntaxSuccess = 0 // HyperMind: cleaned output passes validation
|
|
2822
|
-
let executionSuccess = 0 // Actually executed against cluster
|
|
2823
|
-
let typeErrorsCaught = 0
|
|
2824
|
-
let totalLatency = 0
|
|
2825
|
-
let cleaningRequired = 0 // How many times cleaning was needed
|
|
2826
|
-
|
|
2827
|
-
// Determine provider details
|
|
2828
|
-
const providerInfo = model.includes('claude')
|
|
2829
|
-
? { name: 'Anthropic', modelId: 'claude-sonnet-4-20250514', api: 'https://api.anthropic.com/v1/messages' }
|
|
2830
|
-
: model.includes('gpt')
|
|
2831
|
-
? { name: 'OpenAI', modelId: 'gpt-4o', api: 'https://api.openai.com/v1/chat/completions' }
|
|
2832
|
-
: { name: 'Mock (Pattern Matching)', modelId: 'mock', api: 'N/A' }
|
|
2833
|
-
|
|
2834
|
-
console.log(`\n${'═'.repeat(80)}`)
|
|
2835
|
-
console.log(` HyperMind Agentic Framework Benchmark`)
|
|
2836
|
-
console.log(` Vanilla LLM vs HyperMind Agent Comparison`)
|
|
2837
|
-
console.log(`${'═'.repeat(80)}`)
|
|
2838
|
-
console.log()
|
|
2839
|
-
console.log(` ┌──────────────────────────────────────────────────────────────────────────┐`)
|
|
2840
|
-
console.log(` │ BENCHMARK CONFIGURATION │`)
|
|
2841
|
-
console.log(` ├──────────────────────────────────────────────────────────────────────────┤`)
|
|
2842
|
-
console.log(` │ Dataset: LUBM (Lehigh University Benchmark) Ontology │`)
|
|
2843
|
-
console.log(` │ - 3,272 triples (LUBM-1: 1 university) │`)
|
|
2844
|
-
console.log(` │ - Classes: Professor, GraduateStudent, Course, Department │`)
|
|
2845
|
-
console.log(` │ - Properties: advisor, teacherOf, memberOf, worksFor │`)
|
|
2846
|
-
console.log(` │ │`)
|
|
2847
|
-
console.log(` │ LLM Provider: ${providerInfo.name.padEnd(60)}│`)
|
|
2848
|
-
console.log(` │ Model ID: ${providerInfo.modelId.padEnd(60)}│`)
|
|
2849
|
-
console.log(` │ API Endpoint: ${providerInfo.api.padEnd(60)}│`)
|
|
2850
|
-
console.log(` │ │`)
|
|
2851
|
-
console.log(` │ Task: Natural Language → SPARQL Query Generation │`)
|
|
2852
|
-
console.log(` │ Agent receives question, generates SPARQL, executes query │`)
|
|
2853
|
-
console.log(` │ │`)
|
|
2854
|
-
console.log(` │ Embeddings: NOT USED (this benchmark is NL-to-SPARQL, not semantic) │`)
|
|
2855
|
-
console.log(` │ Multi-Vector: NOT APPLICABLE │`)
|
|
2856
|
-
console.log(` │ │`)
|
|
2857
|
-
console.log(` │ K8s Cluster: ${endpoint.padEnd(60)}│`)
|
|
2858
|
-
console.log(` │ Tests: ${testSuite.length} LUBM queries (Easy: 3, Medium: 5, Hard: 4) │`)
|
|
2859
|
-
console.log(` └──────────────────────────────────────────────────────────────────────────┘`)
|
|
2860
|
-
console.log()
|
|
2861
|
-
console.log(` ┌──────────────────────────────────────────────────────────────────────────┐`)
|
|
2862
|
-
console.log(` │ AGENT CREATION │`)
|
|
2863
|
-
console.log(` ├──────────────────────────────────────────────────────────────────────────┤`)
|
|
2864
|
-
console.log(` │ Name: benchmark-agent │`)
|
|
2865
|
-
console.log(` │ Model: ${model.padEnd(62)}│`)
|
|
2866
|
-
console.log(` │ Tools: kg.sparql.query, kg.motif.find, kg.datalog.apply │`)
|
|
2867
|
-
console.log(` │ Tracing: enabled │`)
|
|
2868
|
-
console.log(` └──────────────────────────────────────────────────────────────────────────┘`)
|
|
2869
|
-
console.log()
|
|
2870
|
-
console.log(` ┌──────────────────────────────────────────────────────────────────────────┐`)
|
|
2871
|
-
console.log(` │ 12 LUBM TEST QUERIES │`)
|
|
2872
|
-
console.log(` ├──────────────────────────────────────────────────────────────────────────┤`)
|
|
2873
|
-
for (const test of testSuite) {
|
|
2874
|
-
const q = `Q${test.index}: "${test.question}"`.slice(0, 72)
|
|
2875
|
-
console.log(` │ ${q.padEnd(74)}│`)
|
|
2876
|
-
}
|
|
2877
|
-
console.log(` └──────────────────────────────────────────────────────────────────────────┘`)
|
|
2878
|
-
console.log()
|
|
2879
|
-
console.log(`${'═'.repeat(80)}\n`)
|
|
2880
|
-
|
|
2881
|
-
// Spawn agent with HyperMind framework
|
|
2882
|
-
const agent = await HyperMindAgent.spawn({
|
|
2883
|
-
name: 'benchmark-agent',
|
|
2884
|
-
model: model,
|
|
2885
|
-
tools: ['kg.sparql.query', 'kg.motif.find', 'kg.datalog.apply'],
|
|
2886
|
-
endpoint: endpoint,
|
|
2887
|
-
timeout: options.timeout || 30000,
|
|
2888
|
-
tracing: true
|
|
2889
|
-
})
|
|
2890
|
-
|
|
2891
|
-
for (const test of testSuite) {
|
|
2892
|
-
const startTime = Date.now()
|
|
2893
|
-
console.log(`Test ${test.index}: ${test.question}`)
|
|
2894
|
-
console.log(` Difficulty: ${test.difficulty}`)
|
|
1372
|
+
// ============================================================================
|
|
1373
|
+
// EXPORTS
|
|
1374
|
+
// ============================================================================
|
|
2895
1375
|
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
const latency = Date.now() - startTime
|
|
2900
|
-
totalLatency += latency
|
|
2901
|
-
|
|
2902
|
-
// Track raw (vanilla) LLM success
|
|
2903
|
-
if (result.rawIsValid === true) {
|
|
2904
|
-
rawSyntaxSuccess++
|
|
2905
|
-
console.log(` 📝 Vanilla LLM: ✅ RAW OUTPUT VALID`)
|
|
2906
|
-
} else if (result.rawIsValid === false) {
|
|
2907
|
-
console.log(` 📝 Vanilla LLM: ❌ RAW OUTPUT INVALID (needs cleaning)`)
|
|
2908
|
-
cleaningRequired++
|
|
2909
|
-
}
|
|
1376
|
+
module.exports = {
|
|
1377
|
+
// Main Agent
|
|
1378
|
+
HyperMindAgent,
|
|
2910
1379
|
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
console.log(` SPARQL: ${result.sparql.slice(0, 60)}...`)
|
|
2918
|
-
}
|
|
2919
|
-
} else {
|
|
2920
|
-
// Check if this was a type error caught by framework
|
|
2921
|
-
if (result.error && result.error.includes('Type')) {
|
|
2922
|
-
typeErrorsCaught++
|
|
2923
|
-
console.log(` 🧠 HyperMind: ⚠️ TYPE ERROR CAUGHT`)
|
|
2924
|
-
} else {
|
|
2925
|
-
console.log(` 🧠 HyperMind: ❌ FAILED - ${result.error}`)
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
1380
|
+
// Supporting Classes
|
|
1381
|
+
MemoryManager,
|
|
1382
|
+
DatalogRuleSet,
|
|
1383
|
+
WasmSandbox,
|
|
1384
|
+
ExecutionTrace,
|
|
1385
|
+
ProofNode,
|
|
2928
1386
|
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
if (options.verbose) {
|
|
2932
|
-
console.log(` ↳ Raw had: ${result.rawSparql.includes('```') ? 'markdown' : 'formatting issues'}`)
|
|
2933
|
-
}
|
|
2934
|
-
}
|
|
1387
|
+
// Type System
|
|
1388
|
+
TypeId,
|
|
2935
1389
|
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
rawIsValid: result.rawIsValid,
|
|
2940
|
-
hypermindSuccess: result.success,
|
|
2941
|
-
executionSuccess: result.success,
|
|
2942
|
-
sparql: result.sparql,
|
|
2943
|
-
rawSparql: result.rawSparql,
|
|
2944
|
-
typeErrorsCaught: result.error?.includes('Type') ? 1 : 0,
|
|
2945
|
-
latencyMs: latency,
|
|
2946
|
-
error: result.error
|
|
2947
|
-
})
|
|
2948
|
-
} catch (error) {
|
|
2949
|
-
console.log(` ❌ ERROR: ${error.message}`)
|
|
2950
|
-
results.push({
|
|
2951
|
-
question: test.question,
|
|
2952
|
-
difficulty: test.difficulty,
|
|
2953
|
-
rawIsValid: false,
|
|
2954
|
-
hypermindSuccess: false,
|
|
2955
|
-
executionSuccess: false,
|
|
2956
|
-
typeErrorsCaught: 0,
|
|
2957
|
-
latencyMs: Date.now() - startTime,
|
|
2958
|
-
error: error.message
|
|
2959
|
-
})
|
|
2960
|
-
}
|
|
1390
|
+
// Agent State Machine
|
|
1391
|
+
AgentState,
|
|
1392
|
+
AgentRuntime,
|
|
2961
1393
|
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
const stats = {
|
|
2967
|
-
totalTests: testSuite.length,
|
|
2968
|
-
// Vanilla LLM stats (raw output without HyperMind)
|
|
2969
|
-
vanillaLlmSyntaxSuccess: rawSyntaxSuccess,
|
|
2970
|
-
vanillaLlmSyntaxRate: (rawSyntaxSuccess / testSuite.length) * 100,
|
|
2971
|
-
// HyperMind stats (with typed tools + cleaning)
|
|
2972
|
-
hypermindSyntaxSuccess: hypermindSyntaxSuccess,
|
|
2973
|
-
hypermindSyntaxRate: (hypermindSyntaxSuccess / testSuite.length) * 100,
|
|
2974
|
-
// Execution stats
|
|
2975
|
-
executionSuccess: executionSuccess,
|
|
2976
|
-
executionSuccessRate: (executionSuccess / testSuite.length) * 100,
|
|
2977
|
-
// Value metrics
|
|
2978
|
-
cleaningRequired: cleaningRequired,
|
|
2979
|
-
syntaxImprovement: hypermindSyntaxSuccess - rawSyntaxSuccess,
|
|
2980
|
-
typeErrorsCaught: typeErrorsCaught,
|
|
2981
|
-
avgLatencyMs: totalLatency / testSuite.length
|
|
2982
|
-
}
|
|
2983
|
-
|
|
2984
|
-
// Print summary with clear comparison
|
|
2985
|
-
console.log(`${'═'.repeat(70)}`)
|
|
2986
|
-
console.log(` BENCHMARK RESULTS: Vanilla LLM vs HyperMind Agent`)
|
|
2987
|
-
console.log(`${'═'.repeat(70)}`)
|
|
2988
|
-
console.log()
|
|
2989
|
-
console.log(` ┌─────────────────────────────────────────────────────────────────┐`)
|
|
2990
|
-
console.log(` │ Metric │ Vanilla LLM │ HyperMind │ Δ Improve │`)
|
|
2991
|
-
console.log(` ├─────────────────────────────────────────────────────────────────┤`)
|
|
2992
|
-
console.log(` │ Syntax Valid │ ${stats.vanillaLlmSyntaxRate.toFixed(1).padStart(9)}% │ ${stats.hypermindSyntaxRate.toFixed(1).padStart(7)}% │ ${stats.syntaxImprovement > 0 ? '+' : ''}${stats.syntaxImprovement.toString().padStart(7)} │`)
|
|
2993
|
-
console.log(` │ Execution Success │ N/A │ ${stats.executionSuccessRate.toFixed(1).padStart(7)}% │ │`)
|
|
2994
|
-
console.log(` │ Avg Latency │ N/A │ ${stats.avgLatencyMs.toFixed(0).padStart(5)}ms │ │`)
|
|
2995
|
-
console.log(` └─────────────────────────────────────────────────────────────────┘`)
|
|
2996
|
-
console.log()
|
|
2997
|
-
console.log(` 📊 Summary:`)
|
|
2998
|
-
console.log(` - Total Tests: ${stats.totalTests}`)
|
|
2999
|
-
console.log(` - Times Cleaning Needed: ${stats.cleaningRequired} (${((stats.cleaningRequired/stats.totalTests)*100).toFixed(0)}%)`)
|
|
3000
|
-
console.log(` - Type Errors Caught: ${stats.typeErrorsCaught}`)
|
|
3001
|
-
if (stats.syntaxImprovement > 0) {
|
|
3002
|
-
console.log(` - HyperMind FIXED ${stats.syntaxImprovement} queries that Vanilla LLM failed!`)
|
|
3003
|
-
}
|
|
3004
|
-
console.log(`${'═'.repeat(70)}\n`)
|
|
3005
|
-
|
|
3006
|
-
// Save results if requested
|
|
3007
|
-
if (options.saveResults) {
|
|
3008
|
-
const fs = require('fs')
|
|
3009
|
-
const filename = `hypermind_benchmark_${model}_${Date.now()}.json`
|
|
3010
|
-
fs.writeFileSync(
|
|
3011
|
-
filename,
|
|
3012
|
-
JSON.stringify(
|
|
3013
|
-
{
|
|
3014
|
-
timestamp: new Date().toISOString(),
|
|
3015
|
-
model,
|
|
3016
|
-
endpoint,
|
|
3017
|
-
comparison: 'Vanilla LLM vs HyperMind Agent',
|
|
3018
|
-
stats,
|
|
3019
|
-
results
|
|
3020
|
-
},
|
|
3021
|
-
null,
|
|
3022
|
-
2
|
|
3023
|
-
)
|
|
3024
|
-
)
|
|
3025
|
-
console.log(`Results saved to: ${filename}`)
|
|
3026
|
-
}
|
|
1394
|
+
// Memory Tiers
|
|
1395
|
+
WorkingMemory,
|
|
1396
|
+
EpisodicMemory,
|
|
1397
|
+
LongTermMemory,
|
|
3027
1398
|
|
|
3028
|
-
|
|
3029
|
-
|
|
1399
|
+
// Governance Layer
|
|
1400
|
+
GovernancePolicy,
|
|
1401
|
+
GovernanceEngine,
|
|
3030
1402
|
|
|
3031
|
-
//
|
|
3032
|
-
|
|
3033
|
-
// Original HyperMind Agent API
|
|
3034
|
-
HyperMindAgent,
|
|
3035
|
-
runHyperMindBenchmark,
|
|
3036
|
-
getHyperMindBenchmarkSuite,
|
|
3037
|
-
validateSparqlSyntax,
|
|
3038
|
-
createPlanningContext,
|
|
3039
|
-
LUBM_TEST_SUITE,
|
|
3040
|
-
HYPERMIND_TOOLS,
|
|
3041
|
-
|
|
3042
|
-
// Architecture Components (v0.5.8+)
|
|
3043
|
-
TypeId, // Type system (Hindley-Milner + Refinement Types)
|
|
3044
|
-
TOOL_REGISTRY, // Typed tool morphisms
|
|
3045
|
-
LLMPlanner, // Natural language -> typed tool pipelines
|
|
3046
|
-
WasmSandbox, // WASM sandbox with capability-based security
|
|
3047
|
-
AgentBuilder, // Fluent builder for agent composition
|
|
3048
|
-
ComposedAgent, // Composed agent with sandbox execution
|
|
3049
|
-
|
|
3050
|
-
// Memory Layer (v0.5.13+) - GraphDB-Powered Agent Memory
|
|
3051
|
-
AgentState, // Agent lifecycle states (CREATED, READY, RUNNING, etc.)
|
|
3052
|
-
AgentRuntime, // Agent identity and state management
|
|
3053
|
-
WorkingMemory, // In-memory current context
|
|
3054
|
-
EpisodicMemory, // Execution history stored in GraphDB
|
|
3055
|
-
LongTermMemory, // Source-of-truth knowledge graph (read-only)
|
|
3056
|
-
MemoryManager, // Unified memory retrieval with weighted scoring
|
|
3057
|
-
|
|
3058
|
-
// Governance Layer (v0.5.13+) - Policy Engine & Capability Grants
|
|
3059
|
-
GovernancePolicy, // Access control and behavioral policies
|
|
3060
|
-
GovernanceEngine, // Policy enforcement and audit logging
|
|
3061
|
-
|
|
3062
|
-
// Scope Layer (v0.5.13+) - Namespace Isolation & Resource Limits
|
|
3063
|
-
AgentScope // Namespace boundaries and resource tracking
|
|
1403
|
+
// Scope Layer
|
|
1404
|
+
AgentScope
|
|
3064
1405
|
}
|