@unrdf/kgc-probe 26.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +414 -0
- package/package.json +81 -0
- package/src/agents/index.mjs +1402 -0
- package/src/artifact.mjs +405 -0
- package/src/cli.mjs +932 -0
- package/src/config.mjs +115 -0
- package/src/guards.mjs +1213 -0
- package/src/index.mjs +347 -0
- package/src/merge.mjs +196 -0
- package/src/observation.mjs +193 -0
- package/src/orchestrator.mjs +315 -0
- package/src/probe.mjs +58 -0
- package/src/probes/CONCURRENCY-PROBE.md +256 -0
- package/src/probes/README.md +275 -0
- package/src/probes/concurrency.mjs +1175 -0
- package/src/probes/filesystem.mjs +731 -0
- package/src/probes/filesystem.test.mjs +244 -0
- package/src/probes/network.mjs +503 -0
- package/src/probes/performance.mjs +816 -0
- package/src/probes/persistence.mjs +785 -0
- package/src/probes/runtime.mjs +589 -0
- package/src/probes/tooling.mjs +454 -0
- package/src/probes/tooling.test.mjs +372 -0
- package/src/probes/verify-execution.mjs +131 -0
- package/src/probes/verify-guards.mjs +73 -0
- package/src/probes/wasm.mjs +715 -0
- package/src/receipt.mjs +197 -0
- package/src/receipts/index.mjs +813 -0
- package/src/reporter.example.mjs +223 -0
- package/src/reporter.mjs +555 -0
- package/src/reporters/markdown.mjs +355 -0
- package/src/reporters/rdf.mjs +383 -0
- package/src/storage/index.mjs +827 -0
- package/src/types.mjs +1028 -0
- package/src/utils/errors.mjs +397 -0
- package/src/utils/index.mjs +32 -0
- package/src/utils/logger.mjs +236 -0
- package/src/vocabulary.ttl +169 -0
|
@@ -0,0 +1,1402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview KGC Probe - Agent Registry and 10 Domain Agents
|
|
3
|
+
*
|
|
4
|
+
* Each agent implements the probe interface:
|
|
5
|
+
* - Agent 1: Orchestrator (merge + receipts coordination)
|
|
6
|
+
* - Agent 2: Runtime (Node version, JS engine, WASM, workers)
|
|
7
|
+
* - Agent 3: Filesystem (roots, max path, file counts, write tests)
|
|
8
|
+
* - Agent 4: WASM (instantiate modules, startup time, memory growth)
|
|
9
|
+
* - Agent 5: Performance (JSON parse, hashing throughput, latency p50/p99)
|
|
10
|
+
* - Agent 6: Network (URL allowlist checks, DNS, max payload)
|
|
11
|
+
* - Agent 7: Tooling (detect accessible commands, versions)
|
|
12
|
+
* - Agent 8: Storage (persistence types, quotas, available bytes)
|
|
13
|
+
* - Agent 9: Concurrency (workers available, parallelism, throttles)
|
|
14
|
+
* - Agent 10: System (platform, OS version, containerized)
|
|
15
|
+
*
|
|
16
|
+
* @module @unrdf/kgc-probe/agents
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { randomUUID } from 'crypto';
|
|
20
|
+
import { performance } from 'perf_hooks';
|
|
21
|
+
import { platform, version, arch, cpus, totalmem, freemem } from 'os';
|
|
22
|
+
import { promises as fs, existsSync } from 'fs';
|
|
23
|
+
import { join } from 'path';
|
|
24
|
+
import { execSync } from 'child_process';
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// OBSERVATION FACTORY
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a standardized observation
|
|
32
|
+
* @param {Object} params - Observation parameters
|
|
33
|
+
* @param {string} params.agent - Agent ID
|
|
34
|
+
* @param {string} params.kind - Observation kind
|
|
35
|
+
* @param {string} params.severity - Severity level
|
|
36
|
+
* @param {string} params.subject - RDF subject
|
|
37
|
+
* @param {Object} params.evidence - Evidence object
|
|
38
|
+
* @param {Object} params.metrics - Metrics object
|
|
39
|
+
* @param {string[]} [params.tags] - Tags
|
|
40
|
+
* @returns {Object} Standardized observation
|
|
41
|
+
*/
|
|
42
|
+
function createObservation({
|
|
43
|
+
agent,
|
|
44
|
+
kind,
|
|
45
|
+
severity = 'info',
|
|
46
|
+
subject,
|
|
47
|
+
predicate,
|
|
48
|
+
object,
|
|
49
|
+
evidence,
|
|
50
|
+
metrics,
|
|
51
|
+
tags = []
|
|
52
|
+
}) {
|
|
53
|
+
return {
|
|
54
|
+
id: randomUUID(),
|
|
55
|
+
agent,
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
kind,
|
|
58
|
+
severity,
|
|
59
|
+
subject,
|
|
60
|
+
predicate,
|
|
61
|
+
object,
|
|
62
|
+
evidence: {
|
|
63
|
+
query: evidence.query || '',
|
|
64
|
+
result: evidence.result || null,
|
|
65
|
+
witnesses: evidence.witnesses || []
|
|
66
|
+
},
|
|
67
|
+
metrics: {
|
|
68
|
+
confidence: metrics.confidence ?? 0.95,
|
|
69
|
+
coverage: metrics.coverage ?? 1.0,
|
|
70
|
+
latency_ms: metrics.latency_ms ?? 0
|
|
71
|
+
},
|
|
72
|
+
tags
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// BASE AGENT CLASS
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Base Agent class with common functionality
|
|
82
|
+
* @class Agent
|
|
83
|
+
*/
|
|
84
|
+
export class Agent {
|
|
85
|
+
/**
|
|
86
|
+
* Create agent
|
|
87
|
+
* @param {string} id - Agent identifier
|
|
88
|
+
* @param {string} domain - Domain/kind of observations
|
|
89
|
+
* @param {string} description - Human description
|
|
90
|
+
*/
|
|
91
|
+
constructor(id, domain, description) {
|
|
92
|
+
/** @type {string} */
|
|
93
|
+
this.id = id;
|
|
94
|
+
/** @type {string} */
|
|
95
|
+
this.domain = domain;
|
|
96
|
+
/** @type {string} */
|
|
97
|
+
this.description = description;
|
|
98
|
+
/** @type {string} */
|
|
99
|
+
this.name = `agent-${id}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Probe the system and produce observations
|
|
104
|
+
* @param {Object} config - Probe configuration
|
|
105
|
+
* @returns {Promise<Array>} Array of observations
|
|
106
|
+
*/
|
|
107
|
+
async probe(config = {}) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Alias for probe (compatibility)
|
|
113
|
+
* @param {Object} config - Probe configuration
|
|
114
|
+
* @returns {Promise<Array>} Array of observations
|
|
115
|
+
*/
|
|
116
|
+
async scan(config = {}) {
|
|
117
|
+
return this.probe(config);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// AGENT 1: ORCHESTRATOR
|
|
123
|
+
// ============================================================================
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* OrchestratorAgent - Merge and receipt coordination
|
|
127
|
+
* @extends Agent
|
|
128
|
+
*/
|
|
129
|
+
export class OrchestratorAgent extends Agent {
|
|
130
|
+
/**
|
|
131
|
+
*
|
|
132
|
+
*/
|
|
133
|
+
constructor() {
|
|
134
|
+
super('orchestrator', 'orchestration', 'Coordinates merge operations and receipt generation');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Probe orchestrator state
|
|
139
|
+
* @param {Object} config - Configuration
|
|
140
|
+
* @returns {Promise<Array>} Observations
|
|
141
|
+
*/
|
|
142
|
+
async probe(config = {}) {
|
|
143
|
+
const startTime = performance.now();
|
|
144
|
+
const observations = [];
|
|
145
|
+
|
|
146
|
+
// Observation 1: Merge capability
|
|
147
|
+
observations.push(createObservation({
|
|
148
|
+
agent: this.id,
|
|
149
|
+
kind: this.domain,
|
|
150
|
+
severity: 'info',
|
|
151
|
+
subject: 'probe:orchestrator',
|
|
152
|
+
predicate: 'probe:hasMergeCapability',
|
|
153
|
+
object: 'true',
|
|
154
|
+
evidence: {
|
|
155
|
+
query: 'MERGE_CAPABILITY_CHECK',
|
|
156
|
+
result: { mergeEnabled: true, algorithm: 'lww-deterministic' },
|
|
157
|
+
witnesses: ['orchestrator:merge-engine']
|
|
158
|
+
},
|
|
159
|
+
metrics: {
|
|
160
|
+
confidence: 1.0,
|
|
161
|
+
coverage: 1.0,
|
|
162
|
+
latency_ms: performance.now() - startTime
|
|
163
|
+
},
|
|
164
|
+
tags: ['orchestrator', 'merge', 'capability']
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
// Observation 2: Receipt capability
|
|
168
|
+
observations.push(createObservation({
|
|
169
|
+
agent: this.id,
|
|
170
|
+
kind: this.domain,
|
|
171
|
+
severity: 'info',
|
|
172
|
+
subject: 'probe:orchestrator',
|
|
173
|
+
predicate: 'probe:hasReceiptCapability',
|
|
174
|
+
object: 'true',
|
|
175
|
+
evidence: {
|
|
176
|
+
query: 'RECEIPT_CAPABILITY_CHECK',
|
|
177
|
+
result: {
|
|
178
|
+
receiptTypes: ['observation', 'merge', 'verification'],
|
|
179
|
+
hashAlgorithm: 'blake3'
|
|
180
|
+
},
|
|
181
|
+
witnesses: ['orchestrator:receipt-chain']
|
|
182
|
+
},
|
|
183
|
+
metrics: {
|
|
184
|
+
confidence: 1.0,
|
|
185
|
+
coverage: 1.0,
|
|
186
|
+
latency_ms: performance.now() - startTime
|
|
187
|
+
},
|
|
188
|
+
tags: ['orchestrator', 'receipt', 'capability']
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
// Observation 3: Agent count
|
|
192
|
+
observations.push(createObservation({
|
|
193
|
+
agent: this.id,
|
|
194
|
+
kind: this.domain,
|
|
195
|
+
severity: 'info',
|
|
196
|
+
subject: 'probe:orchestrator',
|
|
197
|
+
predicate: 'probe:agentCount',
|
|
198
|
+
object: '10',
|
|
199
|
+
evidence: {
|
|
200
|
+
query: 'AGENT_COUNT_CHECK',
|
|
201
|
+
result: { count: 10, domains: this.getAgentDomains() },
|
|
202
|
+
witnesses: []
|
|
203
|
+
},
|
|
204
|
+
metrics: {
|
|
205
|
+
confidence: 1.0,
|
|
206
|
+
coverage: 1.0,
|
|
207
|
+
latency_ms: performance.now() - startTime
|
|
208
|
+
},
|
|
209
|
+
tags: ['orchestrator', 'agents']
|
|
210
|
+
}));
|
|
211
|
+
|
|
212
|
+
return observations;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get list of agent domains
|
|
217
|
+
* @returns {string[]}
|
|
218
|
+
*/
|
|
219
|
+
getAgentDomains() {
|
|
220
|
+
return [
|
|
221
|
+
'orchestration', 'runtime', 'filesystem', 'wasm',
|
|
222
|
+
'performance', 'network', 'tooling', 'storage',
|
|
223
|
+
'concurrency', 'system'
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// AGENT 2: RUNTIME
|
|
230
|
+
// ============================================================================
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* RuntimeAgent - Node version, JS engine, WASM, workers
|
|
234
|
+
* @extends Agent
|
|
235
|
+
*/
|
|
236
|
+
export class RuntimeAgent extends Agent {
|
|
237
|
+
/**
|
|
238
|
+
*
|
|
239
|
+
*/
|
|
240
|
+
constructor() {
|
|
241
|
+
super('runtime', 'runtime', 'Probes Node.js runtime environment');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Probe runtime environment
|
|
246
|
+
* @param {Object} config - Configuration
|
|
247
|
+
* @returns {Promise<Array>} Observations
|
|
248
|
+
*/
|
|
249
|
+
async probe(config = {}) {
|
|
250
|
+
const startTime = performance.now();
|
|
251
|
+
const observations = [];
|
|
252
|
+
|
|
253
|
+
// Observation 1: Node version
|
|
254
|
+
observations.push(createObservation({
|
|
255
|
+
agent: this.id,
|
|
256
|
+
kind: this.domain,
|
|
257
|
+
severity: 'info',
|
|
258
|
+
subject: 'probe:runtime',
|
|
259
|
+
predicate: 'probe:nodeVersion',
|
|
260
|
+
object: process.version,
|
|
261
|
+
evidence: {
|
|
262
|
+
query: 'NODE_VERSION_CHECK',
|
|
263
|
+
result: {
|
|
264
|
+
version: process.version,
|
|
265
|
+
major: parseInt(process.version.slice(1)),
|
|
266
|
+
v8Version: process.versions.v8
|
|
267
|
+
},
|
|
268
|
+
witnesses: ['process.version']
|
|
269
|
+
},
|
|
270
|
+
metrics: {
|
|
271
|
+
confidence: 1.0,
|
|
272
|
+
coverage: 1.0,
|
|
273
|
+
latency_ms: performance.now() - startTime
|
|
274
|
+
},
|
|
275
|
+
tags: ['runtime', 'node', 'version']
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
// Observation 2: V8 Engine
|
|
279
|
+
observations.push(createObservation({
|
|
280
|
+
agent: this.id,
|
|
281
|
+
kind: this.domain,
|
|
282
|
+
severity: 'info',
|
|
283
|
+
subject: 'probe:runtime',
|
|
284
|
+
predicate: 'probe:v8Version',
|
|
285
|
+
object: process.versions.v8,
|
|
286
|
+
evidence: {
|
|
287
|
+
query: 'V8_VERSION_CHECK',
|
|
288
|
+
result: { v8: process.versions.v8, features: this.getV8Features() },
|
|
289
|
+
witnesses: ['process.versions.v8']
|
|
290
|
+
},
|
|
291
|
+
metrics: {
|
|
292
|
+
confidence: 1.0,
|
|
293
|
+
coverage: 1.0,
|
|
294
|
+
latency_ms: performance.now() - startTime
|
|
295
|
+
},
|
|
296
|
+
tags: ['runtime', 'v8', 'engine']
|
|
297
|
+
}));
|
|
298
|
+
|
|
299
|
+
// Observation 3: WASM Support
|
|
300
|
+
const wasmSupported = typeof WebAssembly !== 'undefined';
|
|
301
|
+
observations.push(createObservation({
|
|
302
|
+
agent: this.id,
|
|
303
|
+
kind: this.domain,
|
|
304
|
+
severity: 'info',
|
|
305
|
+
subject: 'probe:runtime',
|
|
306
|
+
predicate: 'probe:wasmSupport',
|
|
307
|
+
object: String(wasmSupported),
|
|
308
|
+
evidence: {
|
|
309
|
+
query: 'WASM_SUPPORT_CHECK',
|
|
310
|
+
result: { supported: wasmSupported },
|
|
311
|
+
witnesses: ['WebAssembly']
|
|
312
|
+
},
|
|
313
|
+
metrics: {
|
|
314
|
+
confidence: 1.0,
|
|
315
|
+
coverage: 1.0,
|
|
316
|
+
latency_ms: performance.now() - startTime
|
|
317
|
+
},
|
|
318
|
+
tags: ['runtime', 'wasm', 'support']
|
|
319
|
+
}));
|
|
320
|
+
|
|
321
|
+
// Observation 4: Worker Threads
|
|
322
|
+
let workersAvailable = false;
|
|
323
|
+
try {
|
|
324
|
+
await import('worker_threads');
|
|
325
|
+
workersAvailable = true;
|
|
326
|
+
} catch {
|
|
327
|
+
workersAvailable = false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
observations.push(createObservation({
|
|
331
|
+
agent: this.id,
|
|
332
|
+
kind: this.domain,
|
|
333
|
+
severity: 'info',
|
|
334
|
+
subject: 'probe:runtime',
|
|
335
|
+
predicate: 'probe:workerThreads',
|
|
336
|
+
object: String(workersAvailable),
|
|
337
|
+
evidence: {
|
|
338
|
+
query: 'WORKER_THREADS_CHECK',
|
|
339
|
+
result: { available: workersAvailable },
|
|
340
|
+
witnesses: ['worker_threads']
|
|
341
|
+
},
|
|
342
|
+
metrics: {
|
|
343
|
+
confidence: 1.0,
|
|
344
|
+
coverage: 1.0,
|
|
345
|
+
latency_ms: performance.now() - startTime
|
|
346
|
+
},
|
|
347
|
+
tags: ['runtime', 'workers', 'threads']
|
|
348
|
+
}));
|
|
349
|
+
|
|
350
|
+
return observations;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get V8 feature flags
|
|
355
|
+
* @returns {Object}
|
|
356
|
+
*/
|
|
357
|
+
getV8Features() {
|
|
358
|
+
return {
|
|
359
|
+
hasPromise: typeof Promise !== 'undefined',
|
|
360
|
+
hasAsyncIterator: typeof Symbol.asyncIterator !== 'undefined',
|
|
361
|
+
hasBigInt: typeof BigInt !== 'undefined',
|
|
362
|
+
hasOptionalChaining: true
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ============================================================================
|
|
368
|
+
// AGENT 3: FILESYSTEM
|
|
369
|
+
// ============================================================================
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* FilesystemAgent - Filesystem probing
|
|
373
|
+
* @extends Agent
|
|
374
|
+
*/
|
|
375
|
+
export class FilesystemAgent extends Agent {
|
|
376
|
+
/**
|
|
377
|
+
*
|
|
378
|
+
*/
|
|
379
|
+
constructor() {
|
|
380
|
+
super('filesystem', 'filesystem', 'Probes filesystem capabilities and limits');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Probe filesystem
|
|
385
|
+
* @param {Object} config - Configuration
|
|
386
|
+
* @returns {Promise<Array>} Observations
|
|
387
|
+
*/
|
|
388
|
+
async probe(config = {}) {
|
|
389
|
+
const startTime = performance.now();
|
|
390
|
+
const observations = [];
|
|
391
|
+
const allowedRoots = config.fsRoots || [process.cwd()];
|
|
392
|
+
|
|
393
|
+
// Observation 1: Working directory
|
|
394
|
+
observations.push(createObservation({
|
|
395
|
+
agent: this.id,
|
|
396
|
+
kind: this.domain,
|
|
397
|
+
severity: 'info',
|
|
398
|
+
subject: 'probe:filesystem',
|
|
399
|
+
predicate: 'probe:workingDirectory',
|
|
400
|
+
object: process.cwd(),
|
|
401
|
+
evidence: {
|
|
402
|
+
query: 'CWD_CHECK',
|
|
403
|
+
result: { cwd: process.cwd(), readable: true },
|
|
404
|
+
witnesses: ['process.cwd()']
|
|
405
|
+
},
|
|
406
|
+
metrics: {
|
|
407
|
+
confidence: 1.0,
|
|
408
|
+
coverage: 1.0,
|
|
409
|
+
latency_ms: performance.now() - startTime
|
|
410
|
+
},
|
|
411
|
+
tags: ['filesystem', 'cwd']
|
|
412
|
+
}));
|
|
413
|
+
|
|
414
|
+
// Observation 2: Max path length
|
|
415
|
+
const maxPath = process.platform === 'win32' ? 260 : 4096;
|
|
416
|
+
observations.push(createObservation({
|
|
417
|
+
agent: this.id,
|
|
418
|
+
kind: this.domain,
|
|
419
|
+
severity: 'info',
|
|
420
|
+
subject: 'probe:filesystem',
|
|
421
|
+
predicate: 'probe:maxPathLength',
|
|
422
|
+
object: String(maxPath),
|
|
423
|
+
evidence: {
|
|
424
|
+
query: 'MAX_PATH_CHECK',
|
|
425
|
+
result: { maxPath, platform: process.platform },
|
|
426
|
+
witnesses: ['os.platform']
|
|
427
|
+
},
|
|
428
|
+
metrics: {
|
|
429
|
+
confidence: 0.95,
|
|
430
|
+
coverage: 1.0,
|
|
431
|
+
latency_ms: performance.now() - startTime
|
|
432
|
+
},
|
|
433
|
+
tags: ['filesystem', 'limits']
|
|
434
|
+
}));
|
|
435
|
+
|
|
436
|
+
// Observation 3: Write test
|
|
437
|
+
let writeCapable = false;
|
|
438
|
+
const testFile = join(process.cwd(), `.probe-write-test-${Date.now()}`);
|
|
439
|
+
try {
|
|
440
|
+
await fs.writeFile(testFile, 'test');
|
|
441
|
+
await fs.unlink(testFile);
|
|
442
|
+
writeCapable = true;
|
|
443
|
+
} catch {
|
|
444
|
+
writeCapable = false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
observations.push(createObservation({
|
|
448
|
+
agent: this.id,
|
|
449
|
+
kind: this.domain,
|
|
450
|
+
severity: writeCapable ? 'info' : 'warning',
|
|
451
|
+
subject: 'probe:filesystem',
|
|
452
|
+
predicate: 'probe:writeCapable',
|
|
453
|
+
object: String(writeCapable),
|
|
454
|
+
evidence: {
|
|
455
|
+
query: 'WRITE_TEST',
|
|
456
|
+
result: { writeCapable, testDir: process.cwd() },
|
|
457
|
+
witnesses: ['fs.writeFile']
|
|
458
|
+
},
|
|
459
|
+
metrics: {
|
|
460
|
+
confidence: 1.0,
|
|
461
|
+
coverage: 1.0,
|
|
462
|
+
latency_ms: performance.now() - startTime
|
|
463
|
+
},
|
|
464
|
+
tags: ['filesystem', 'write', 'capability']
|
|
465
|
+
}));
|
|
466
|
+
|
|
467
|
+
// Observation 4: File count in allowed roots
|
|
468
|
+
for (const root of allowedRoots.slice(0, 3)) {
|
|
469
|
+
if (existsSync(root)) {
|
|
470
|
+
try {
|
|
471
|
+
const files = await fs.readdir(root);
|
|
472
|
+
observations.push(createObservation({
|
|
473
|
+
agent: this.id,
|
|
474
|
+
kind: this.domain,
|
|
475
|
+
severity: 'info',
|
|
476
|
+
subject: `probe:filesystem:${root}`,
|
|
477
|
+
predicate: 'probe:fileCount',
|
|
478
|
+
object: String(files.length),
|
|
479
|
+
evidence: {
|
|
480
|
+
query: 'FILE_COUNT',
|
|
481
|
+
result: { root, count: files.length },
|
|
482
|
+
witnesses: files.slice(0, 5)
|
|
483
|
+
},
|
|
484
|
+
metrics: {
|
|
485
|
+
confidence: 1.0,
|
|
486
|
+
coverage: 0.8,
|
|
487
|
+
latency_ms: performance.now() - startTime
|
|
488
|
+
},
|
|
489
|
+
tags: ['filesystem', 'count', root]
|
|
490
|
+
}));
|
|
491
|
+
} catch (err) {
|
|
492
|
+
// Skip unreadable directories
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return observations;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ============================================================================
|
|
502
|
+
// AGENT 4: WASM
|
|
503
|
+
// ============================================================================
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* WasmAgent - WASM capabilities
|
|
507
|
+
* @extends Agent
|
|
508
|
+
*/
|
|
509
|
+
export class WasmAgent extends Agent {
|
|
510
|
+
/**
|
|
511
|
+
*
|
|
512
|
+
*/
|
|
513
|
+
constructor() {
|
|
514
|
+
super('wasm', 'wasm', 'Probes WebAssembly capabilities');
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Probe WASM capabilities
|
|
519
|
+
* @param {Object} config - Configuration
|
|
520
|
+
* @returns {Promise<Array>} Observations
|
|
521
|
+
*/
|
|
522
|
+
async probe(config = {}) {
|
|
523
|
+
const startTime = performance.now();
|
|
524
|
+
const observations = [];
|
|
525
|
+
|
|
526
|
+
// Check WASM availability
|
|
527
|
+
const wasmAvailable = typeof WebAssembly !== 'undefined';
|
|
528
|
+
|
|
529
|
+
observations.push(createObservation({
|
|
530
|
+
agent: this.id,
|
|
531
|
+
kind: this.domain,
|
|
532
|
+
severity: 'info',
|
|
533
|
+
subject: 'probe:wasm',
|
|
534
|
+
predicate: 'probe:available',
|
|
535
|
+
object: String(wasmAvailable),
|
|
536
|
+
evidence: {
|
|
537
|
+
query: 'WASM_AVAILABLE',
|
|
538
|
+
result: { available: wasmAvailable },
|
|
539
|
+
witnesses: ['WebAssembly']
|
|
540
|
+
},
|
|
541
|
+
metrics: {
|
|
542
|
+
confidence: 1.0,
|
|
543
|
+
coverage: 1.0,
|
|
544
|
+
latency_ms: performance.now() - startTime
|
|
545
|
+
},
|
|
546
|
+
tags: ['wasm', 'available']
|
|
547
|
+
}));
|
|
548
|
+
|
|
549
|
+
if (wasmAvailable) {
|
|
550
|
+
// Test WASM instantiation with minimal module
|
|
551
|
+
const wasmBytes = new Uint8Array([
|
|
552
|
+
0x00, 0x61, 0x73, 0x6d, // Magic
|
|
553
|
+
0x01, 0x00, 0x00, 0x00 // Version
|
|
554
|
+
]);
|
|
555
|
+
|
|
556
|
+
let instantiationTime = 0;
|
|
557
|
+
try {
|
|
558
|
+
const instStart = performance.now();
|
|
559
|
+
await WebAssembly.compile(wasmBytes);
|
|
560
|
+
instantiationTime = performance.now() - instStart;
|
|
561
|
+
} catch {
|
|
562
|
+
instantiationTime = -1;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
observations.push(createObservation({
|
|
566
|
+
agent: this.id,
|
|
567
|
+
kind: this.domain,
|
|
568
|
+
severity: 'info',
|
|
569
|
+
subject: 'probe:wasm',
|
|
570
|
+
predicate: 'probe:compileTime',
|
|
571
|
+
object: String(instantiationTime.toFixed(2)),
|
|
572
|
+
evidence: {
|
|
573
|
+
query: 'WASM_COMPILE_TIME',
|
|
574
|
+
result: { time_ms: instantiationTime, success: instantiationTime >= 0 },
|
|
575
|
+
witnesses: ['WebAssembly.compile']
|
|
576
|
+
},
|
|
577
|
+
metrics: {
|
|
578
|
+
confidence: 1.0,
|
|
579
|
+
coverage: 1.0,
|
|
580
|
+
latency_ms: performance.now() - startTime
|
|
581
|
+
},
|
|
582
|
+
tags: ['wasm', 'compile', 'performance']
|
|
583
|
+
}));
|
|
584
|
+
|
|
585
|
+
// Memory growth test
|
|
586
|
+
let maxMemory = 0;
|
|
587
|
+
try {
|
|
588
|
+
const memory = new WebAssembly.Memory({ initial: 1, maximum: 100 });
|
|
589
|
+
maxMemory = memory.buffer.byteLength;
|
|
590
|
+
} catch {
|
|
591
|
+
maxMemory = 0;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
observations.push(createObservation({
|
|
595
|
+
agent: this.id,
|
|
596
|
+
kind: this.domain,
|
|
597
|
+
severity: 'info',
|
|
598
|
+
subject: 'probe:wasm',
|
|
599
|
+
predicate: 'probe:initialMemory',
|
|
600
|
+
object: String(maxMemory),
|
|
601
|
+
evidence: {
|
|
602
|
+
query: 'WASM_MEMORY',
|
|
603
|
+
result: { initial_bytes: maxMemory, pages: maxMemory / 65536 },
|
|
604
|
+
witnesses: ['WebAssembly.Memory']
|
|
605
|
+
},
|
|
606
|
+
metrics: {
|
|
607
|
+
confidence: 1.0,
|
|
608
|
+
coverage: 1.0,
|
|
609
|
+
latency_ms: performance.now() - startTime
|
|
610
|
+
},
|
|
611
|
+
tags: ['wasm', 'memory']
|
|
612
|
+
}));
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return observations;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// ============================================================================
|
|
620
|
+
// AGENT 5: PERFORMANCE
|
|
621
|
+
// ============================================================================
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* PerformanceAgent - Performance benchmarks
|
|
625
|
+
* @extends Agent
|
|
626
|
+
*/
|
|
627
|
+
export class PerformanceAgent extends Agent {
|
|
628
|
+
/**
|
|
629
|
+
*
|
|
630
|
+
*/
|
|
631
|
+
constructor() {
|
|
632
|
+
super('performance', 'performance', 'Benchmarks JSON parsing, hashing, and latency');
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Probe performance
|
|
637
|
+
* @param {Object} config - Configuration
|
|
638
|
+
* @returns {Promise<Array>} Observations
|
|
639
|
+
*/
|
|
640
|
+
async probe(config = {}) {
|
|
641
|
+
const startTime = performance.now();
|
|
642
|
+
const observations = [];
|
|
643
|
+
|
|
644
|
+
// JSON parse benchmark
|
|
645
|
+
const testData = JSON.stringify({ data: Array(1000).fill({ key: 'value', num: 42 }) });
|
|
646
|
+
const jsonStart = performance.now();
|
|
647
|
+
for (let i = 0; i < 100; i++) {
|
|
648
|
+
JSON.parse(testData);
|
|
649
|
+
}
|
|
650
|
+
const jsonTime = (performance.now() - jsonStart) / 100;
|
|
651
|
+
|
|
652
|
+
observations.push(createObservation({
|
|
653
|
+
agent: this.id,
|
|
654
|
+
kind: this.domain,
|
|
655
|
+
severity: 'info',
|
|
656
|
+
subject: 'probe:performance',
|
|
657
|
+
predicate: 'probe:jsonParseTime',
|
|
658
|
+
object: String(jsonTime.toFixed(3)),
|
|
659
|
+
evidence: {
|
|
660
|
+
query: 'JSON_PARSE_BENCH',
|
|
661
|
+
result: { avg_ms: jsonTime, iterations: 100, dataSize: testData.length },
|
|
662
|
+
witnesses: ['JSON.parse']
|
|
663
|
+
},
|
|
664
|
+
metrics: {
|
|
665
|
+
confidence: 0.95,
|
|
666
|
+
coverage: 1.0,
|
|
667
|
+
latency_ms: performance.now() - startTime
|
|
668
|
+
},
|
|
669
|
+
tags: ['performance', 'json', 'benchmark']
|
|
670
|
+
}));
|
|
671
|
+
|
|
672
|
+
// Simple hash benchmark
|
|
673
|
+
const hashStart = performance.now();
|
|
674
|
+
let hashSum = 0;
|
|
675
|
+
for (let i = 0; i < 10000; i++) {
|
|
676
|
+
let hash = 0;
|
|
677
|
+
for (let j = 0; j < testData.length; j++) {
|
|
678
|
+
hash = ((hash << 5) - hash) + testData.charCodeAt(j);
|
|
679
|
+
hash = hash & hash;
|
|
680
|
+
}
|
|
681
|
+
hashSum += hash;
|
|
682
|
+
}
|
|
683
|
+
const hashTime = (performance.now() - hashStart) / 10000;
|
|
684
|
+
|
|
685
|
+
observations.push(createObservation({
|
|
686
|
+
agent: this.id,
|
|
687
|
+
kind: this.domain,
|
|
688
|
+
severity: 'info',
|
|
689
|
+
subject: 'probe:performance',
|
|
690
|
+
predicate: 'probe:hashThroughput',
|
|
691
|
+
object: String((testData.length / hashTime / 1000).toFixed(2)),
|
|
692
|
+
evidence: {
|
|
693
|
+
query: 'HASH_BENCH',
|
|
694
|
+
result: { throughput_mb_s: testData.length / hashTime / 1000, iterations: 10000 },
|
|
695
|
+
witnesses: ['hash-loop']
|
|
696
|
+
},
|
|
697
|
+
metrics: {
|
|
698
|
+
confidence: 0.9,
|
|
699
|
+
coverage: 1.0,
|
|
700
|
+
latency_ms: performance.now() - startTime
|
|
701
|
+
},
|
|
702
|
+
tags: ['performance', 'hash', 'throughput']
|
|
703
|
+
}));
|
|
704
|
+
|
|
705
|
+
// Timer resolution
|
|
706
|
+
const timings = [];
|
|
707
|
+
for (let i = 0; i < 100; i++) {
|
|
708
|
+
const t1 = performance.now();
|
|
709
|
+
const t2 = performance.now();
|
|
710
|
+
timings.push(t2 - t1);
|
|
711
|
+
}
|
|
712
|
+
timings.sort((a, b) => a - b);
|
|
713
|
+
const p50 = timings[50];
|
|
714
|
+
const p99 = timings[99];
|
|
715
|
+
|
|
716
|
+
observations.push(createObservation({
|
|
717
|
+
agent: this.id,
|
|
718
|
+
kind: this.domain,
|
|
719
|
+
severity: 'info',
|
|
720
|
+
subject: 'probe:performance',
|
|
721
|
+
predicate: 'probe:timerResolution',
|
|
722
|
+
object: `p50=${p50.toFixed(6)},p99=${p99.toFixed(6)}`,
|
|
723
|
+
evidence: {
|
|
724
|
+
query: 'TIMER_RESOLUTION',
|
|
725
|
+
result: { p50, p99, samples: 100 },
|
|
726
|
+
witnesses: ['performance.now']
|
|
727
|
+
},
|
|
728
|
+
metrics: {
|
|
729
|
+
confidence: 0.95,
|
|
730
|
+
coverage: 1.0,
|
|
731
|
+
latency_ms: performance.now() - startTime
|
|
732
|
+
},
|
|
733
|
+
tags: ['performance', 'latency', 'timer']
|
|
734
|
+
}));
|
|
735
|
+
|
|
736
|
+
return observations;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// ============================================================================
|
|
741
|
+
// AGENT 6: NETWORK
|
|
742
|
+
// ============================================================================
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* NetworkAgent - Network capabilities
|
|
746
|
+
* @extends Agent
|
|
747
|
+
*/
|
|
748
|
+
export class NetworkAgent extends Agent {
|
|
749
|
+
/**
|
|
750
|
+
*
|
|
751
|
+
*/
|
|
752
|
+
constructor() {
|
|
753
|
+
super('network', 'network', 'Probes network capabilities and restrictions');
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Probe network
|
|
758
|
+
* @param {Object} config - Configuration
|
|
759
|
+
* @returns {Promise<Array>} Observations
|
|
760
|
+
*/
|
|
761
|
+
async probe(config = {}) {
|
|
762
|
+
const startTime = performance.now();
|
|
763
|
+
const observations = [];
|
|
764
|
+
const allowlist = config.networkAllowlist || [];
|
|
765
|
+
|
|
766
|
+
// Observation 1: Network available
|
|
767
|
+
const dns = await import('dns').catch(() => null);
|
|
768
|
+
const netAvailable = dns !== null;
|
|
769
|
+
|
|
770
|
+
observations.push(createObservation({
|
|
771
|
+
agent: this.id,
|
|
772
|
+
kind: this.domain,
|
|
773
|
+
severity: 'info',
|
|
774
|
+
subject: 'probe:network',
|
|
775
|
+
predicate: 'probe:dnsAvailable',
|
|
776
|
+
object: String(netAvailable),
|
|
777
|
+
evidence: {
|
|
778
|
+
query: 'DNS_AVAILABLE',
|
|
779
|
+
result: { available: netAvailable },
|
|
780
|
+
witnesses: ['dns']
|
|
781
|
+
},
|
|
782
|
+
metrics: {
|
|
783
|
+
confidence: 1.0,
|
|
784
|
+
coverage: 1.0,
|
|
785
|
+
latency_ms: performance.now() - startTime
|
|
786
|
+
},
|
|
787
|
+
tags: ['network', 'dns']
|
|
788
|
+
}));
|
|
789
|
+
|
|
790
|
+
// Observation 2: Allowlist check
|
|
791
|
+
observations.push(createObservation({
|
|
792
|
+
agent: this.id,
|
|
793
|
+
kind: this.domain,
|
|
794
|
+
severity: 'info',
|
|
795
|
+
subject: 'probe:network',
|
|
796
|
+
predicate: 'probe:allowlistSize',
|
|
797
|
+
object: String(allowlist.length),
|
|
798
|
+
evidence: {
|
|
799
|
+
query: 'ALLOWLIST_CHECK',
|
|
800
|
+
result: { count: allowlist.length, urls: allowlist.slice(0, 5) },
|
|
801
|
+
witnesses: allowlist.slice(0, 3)
|
|
802
|
+
},
|
|
803
|
+
metrics: {
|
|
804
|
+
confidence: 1.0,
|
|
805
|
+
coverage: 1.0,
|
|
806
|
+
latency_ms: performance.now() - startTime
|
|
807
|
+
},
|
|
808
|
+
tags: ['network', 'allowlist']
|
|
809
|
+
}));
|
|
810
|
+
|
|
811
|
+
// Observation 3: Max payload (simulated)
|
|
812
|
+
const maxPayload = 10 * 1024 * 1024; // 10MB default
|
|
813
|
+
observations.push(createObservation({
|
|
814
|
+
agent: this.id,
|
|
815
|
+
kind: this.domain,
|
|
816
|
+
severity: 'info',
|
|
817
|
+
subject: 'probe:network',
|
|
818
|
+
predicate: 'probe:maxPayload',
|
|
819
|
+
object: String(maxPayload),
|
|
820
|
+
evidence: {
|
|
821
|
+
query: 'MAX_PAYLOAD',
|
|
822
|
+
result: { bytes: maxPayload, mb: maxPayload / (1024 * 1024) },
|
|
823
|
+
witnesses: ['config.maxPayload']
|
|
824
|
+
},
|
|
825
|
+
metrics: {
|
|
826
|
+
confidence: 0.8,
|
|
827
|
+
coverage: 1.0,
|
|
828
|
+
latency_ms: performance.now() - startTime
|
|
829
|
+
},
|
|
830
|
+
tags: ['network', 'limits']
|
|
831
|
+
}));
|
|
832
|
+
|
|
833
|
+
return observations;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// ============================================================================
|
|
838
|
+
// AGENT 7: TOOLING
|
|
839
|
+
// ============================================================================
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* ToolingAgent - Command availability
|
|
843
|
+
* @extends Agent
|
|
844
|
+
*/
|
|
845
|
+
export class ToolingAgent extends Agent {
|
|
846
|
+
/**
|
|
847
|
+
*
|
|
848
|
+
*/
|
|
849
|
+
constructor() {
|
|
850
|
+
super('tooling', 'tooling', 'Detects available command-line tools');
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Probe tooling
|
|
855
|
+
* @param {Object} config - Configuration
|
|
856
|
+
* @returns {Promise<Array>} Observations
|
|
857
|
+
*/
|
|
858
|
+
async probe(config = {}) {
|
|
859
|
+
const startTime = performance.now();
|
|
860
|
+
const observations = [];
|
|
861
|
+
|
|
862
|
+
const tools = ['node', 'npm', 'git', 'pnpm'];
|
|
863
|
+
|
|
864
|
+
for (const tool of tools) {
|
|
865
|
+
let available = false;
|
|
866
|
+
let version = 'unknown';
|
|
867
|
+
|
|
868
|
+
try {
|
|
869
|
+
version = execSync(`${tool} --version 2>/dev/null`, { encoding: 'utf8', timeout: 2000 }).trim().split('\n')[0];
|
|
870
|
+
available = true;
|
|
871
|
+
} catch {
|
|
872
|
+
available = false;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
observations.push(createObservation({
|
|
876
|
+
agent: this.id,
|
|
877
|
+
kind: this.domain,
|
|
878
|
+
severity: 'info',
|
|
879
|
+
subject: `probe:tooling:${tool}`,
|
|
880
|
+
predicate: 'probe:available',
|
|
881
|
+
object: String(available),
|
|
882
|
+
evidence: {
|
|
883
|
+
query: `TOOL_CHECK_${tool.toUpperCase()}`,
|
|
884
|
+
result: { tool, available, version },
|
|
885
|
+
witnesses: [tool]
|
|
886
|
+
},
|
|
887
|
+
metrics: {
|
|
888
|
+
confidence: 1.0,
|
|
889
|
+
coverage: 1.0,
|
|
890
|
+
latency_ms: performance.now() - startTime
|
|
891
|
+
},
|
|
892
|
+
tags: ['tooling', tool]
|
|
893
|
+
}));
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
return observations;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// ============================================================================
|
|
901
|
+
// AGENT 8: STORAGE
|
|
902
|
+
// ============================================================================
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* StorageAgent - Storage capabilities
|
|
906
|
+
* @extends Agent
|
|
907
|
+
*/
|
|
908
|
+
export class StorageAgent extends Agent {
|
|
909
|
+
/**
|
|
910
|
+
*
|
|
911
|
+
*/
|
|
912
|
+
constructor() {
|
|
913
|
+
super('storage', 'storage', 'Probes storage persistence and quotas');
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Probe storage
|
|
918
|
+
* @param {Object} config - Configuration
|
|
919
|
+
* @returns {Promise<Array>} Observations
|
|
920
|
+
*/
|
|
921
|
+
async probe(config = {}) {
|
|
922
|
+
const startTime = performance.now();
|
|
923
|
+
const observations = [];
|
|
924
|
+
|
|
925
|
+
// Available memory as proxy for storage
|
|
926
|
+
const freeBytes = freemem();
|
|
927
|
+
const totalBytes = totalmem();
|
|
928
|
+
|
|
929
|
+
observations.push(createObservation({
|
|
930
|
+
agent: this.id,
|
|
931
|
+
kind: this.domain,
|
|
932
|
+
severity: 'info',
|
|
933
|
+
subject: 'probe:storage',
|
|
934
|
+
predicate: 'probe:availableBytes',
|
|
935
|
+
object: String(freeBytes),
|
|
936
|
+
evidence: {
|
|
937
|
+
query: 'FREE_MEMORY',
|
|
938
|
+
result: { free: freeBytes, total: totalBytes, ratio: freeBytes / totalBytes },
|
|
939
|
+
witnesses: ['os.freemem']
|
|
940
|
+
},
|
|
941
|
+
metrics: {
|
|
942
|
+
confidence: 0.9,
|
|
943
|
+
coverage: 1.0,
|
|
944
|
+
latency_ms: performance.now() - startTime
|
|
945
|
+
},
|
|
946
|
+
tags: ['storage', 'memory']
|
|
947
|
+
}));
|
|
948
|
+
|
|
949
|
+
// Persistence types
|
|
950
|
+
const persistenceTypes = ['memory', 'file', 'database'];
|
|
951
|
+
observations.push(createObservation({
|
|
952
|
+
agent: this.id,
|
|
953
|
+
kind: this.domain,
|
|
954
|
+
severity: 'info',
|
|
955
|
+
subject: 'probe:storage',
|
|
956
|
+
predicate: 'probe:persistenceTypes',
|
|
957
|
+
object: persistenceTypes.join(','),
|
|
958
|
+
evidence: {
|
|
959
|
+
query: 'PERSISTENCE_TYPES',
|
|
960
|
+
result: { types: persistenceTypes },
|
|
961
|
+
witnesses: []
|
|
962
|
+
},
|
|
963
|
+
metrics: {
|
|
964
|
+
confidence: 1.0,
|
|
965
|
+
coverage: 1.0,
|
|
966
|
+
latency_ms: performance.now() - startTime
|
|
967
|
+
},
|
|
968
|
+
tags: ['storage', 'persistence']
|
|
969
|
+
}));
|
|
970
|
+
|
|
971
|
+
// Temp directory
|
|
972
|
+
const tmpDir = process.env.TMPDIR || process.env.TMP || '/tmp';
|
|
973
|
+
observations.push(createObservation({
|
|
974
|
+
agent: this.id,
|
|
975
|
+
kind: this.domain,
|
|
976
|
+
severity: 'info',
|
|
977
|
+
subject: 'probe:storage',
|
|
978
|
+
predicate: 'probe:tempDirectory',
|
|
979
|
+
object: tmpDir,
|
|
980
|
+
evidence: {
|
|
981
|
+
query: 'TEMP_DIR',
|
|
982
|
+
result: { path: tmpDir, exists: existsSync(tmpDir) },
|
|
983
|
+
witnesses: ['process.env.TMPDIR']
|
|
984
|
+
},
|
|
985
|
+
metrics: {
|
|
986
|
+
confidence: 1.0,
|
|
987
|
+
coverage: 1.0,
|
|
988
|
+
latency_ms: performance.now() - startTime
|
|
989
|
+
},
|
|
990
|
+
tags: ['storage', 'temp']
|
|
991
|
+
}));
|
|
992
|
+
|
|
993
|
+
return observations;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// ============================================================================
|
|
998
|
+
// AGENT 9: CONCURRENCY
|
|
999
|
+
// ============================================================================
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* ConcurrencyAgent - Concurrency capabilities
|
|
1003
|
+
* @extends Agent
|
|
1004
|
+
*/
|
|
1005
|
+
export class ConcurrencyAgent extends Agent {
|
|
1006
|
+
/**
|
|
1007
|
+
*
|
|
1008
|
+
*/
|
|
1009
|
+
constructor() {
|
|
1010
|
+
super('concurrency', 'concurrency', 'Probes worker availability and parallelism');
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/**
|
|
1014
|
+
* Probe concurrency
|
|
1015
|
+
* @param {Object} config - Configuration
|
|
1016
|
+
* @returns {Promise<Array>} Observations
|
|
1017
|
+
*/
|
|
1018
|
+
async probe(config = {}) {
|
|
1019
|
+
const startTime = performance.now();
|
|
1020
|
+
const observations = [];
|
|
1021
|
+
|
|
1022
|
+
// CPU count (parallelism potential)
|
|
1023
|
+
const cpuCount = cpus().length;
|
|
1024
|
+
observations.push(createObservation({
|
|
1025
|
+
agent: this.id,
|
|
1026
|
+
kind: this.domain,
|
|
1027
|
+
severity: 'info',
|
|
1028
|
+
subject: 'probe:concurrency',
|
|
1029
|
+
predicate: 'probe:cpuCount',
|
|
1030
|
+
object: String(cpuCount),
|
|
1031
|
+
evidence: {
|
|
1032
|
+
query: 'CPU_COUNT',
|
|
1033
|
+
result: { count: cpuCount, model: cpus()[0]?.model },
|
|
1034
|
+
witnesses: ['os.cpus']
|
|
1035
|
+
},
|
|
1036
|
+
metrics: {
|
|
1037
|
+
confidence: 1.0,
|
|
1038
|
+
coverage: 1.0,
|
|
1039
|
+
latency_ms: performance.now() - startTime
|
|
1040
|
+
},
|
|
1041
|
+
tags: ['concurrency', 'cpu']
|
|
1042
|
+
}));
|
|
1043
|
+
|
|
1044
|
+
// Worker threads capability
|
|
1045
|
+
let workersAvailable = false;
|
|
1046
|
+
try {
|
|
1047
|
+
await import('worker_threads');
|
|
1048
|
+
workersAvailable = true;
|
|
1049
|
+
} catch {
|
|
1050
|
+
workersAvailable = false;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
observations.push(createObservation({
|
|
1054
|
+
agent: this.id,
|
|
1055
|
+
kind: this.domain,
|
|
1056
|
+
severity: 'info',
|
|
1057
|
+
subject: 'probe:concurrency',
|
|
1058
|
+
predicate: 'probe:workerThreadsAvailable',
|
|
1059
|
+
object: String(workersAvailable),
|
|
1060
|
+
evidence: {
|
|
1061
|
+
query: 'WORKER_THREADS',
|
|
1062
|
+
result: { available: workersAvailable },
|
|
1063
|
+
witnesses: ['worker_threads']
|
|
1064
|
+
},
|
|
1065
|
+
metrics: {
|
|
1066
|
+
confidence: 1.0,
|
|
1067
|
+
coverage: 1.0,
|
|
1068
|
+
latency_ms: performance.now() - startTime
|
|
1069
|
+
},
|
|
1070
|
+
tags: ['concurrency', 'workers']
|
|
1071
|
+
}));
|
|
1072
|
+
|
|
1073
|
+
// Event loop latency
|
|
1074
|
+
const loopStart = performance.now();
|
|
1075
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
1076
|
+
const loopLatency = performance.now() - loopStart;
|
|
1077
|
+
|
|
1078
|
+
observations.push(createObservation({
|
|
1079
|
+
agent: this.id,
|
|
1080
|
+
kind: this.domain,
|
|
1081
|
+
severity: loopLatency > 10 ? 'warning' : 'info',
|
|
1082
|
+
subject: 'probe:concurrency',
|
|
1083
|
+
predicate: 'probe:eventLoopLatency',
|
|
1084
|
+
object: String(loopLatency.toFixed(3)),
|
|
1085
|
+
evidence: {
|
|
1086
|
+
query: 'EVENT_LOOP_LATENCY',
|
|
1087
|
+
result: { latency_ms: loopLatency },
|
|
1088
|
+
witnesses: ['setImmediate']
|
|
1089
|
+
},
|
|
1090
|
+
metrics: {
|
|
1091
|
+
confidence: 0.9,
|
|
1092
|
+
coverage: 1.0,
|
|
1093
|
+
latency_ms: performance.now() - startTime
|
|
1094
|
+
},
|
|
1095
|
+
tags: ['concurrency', 'eventloop']
|
|
1096
|
+
}));
|
|
1097
|
+
|
|
1098
|
+
return observations;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// ============================================================================
|
|
1103
|
+
// AGENT 10: SYSTEM
|
|
1104
|
+
// ============================================================================
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* SystemAgent - System information
|
|
1108
|
+
* @extends Agent
|
|
1109
|
+
*/
|
|
1110
|
+
export class SystemAgent extends Agent {
|
|
1111
|
+
/**
|
|
1112
|
+
*
|
|
1113
|
+
*/
|
|
1114
|
+
constructor() {
|
|
1115
|
+
super('system', 'system', 'Probes platform, OS version, and container status');
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Probe system
|
|
1120
|
+
* @param {Object} config - Configuration
|
|
1121
|
+
* @returns {Promise<Array>} Observations
|
|
1122
|
+
*/
|
|
1123
|
+
async probe(config = {}) {
|
|
1124
|
+
const startTime = performance.now();
|
|
1125
|
+
const observations = [];
|
|
1126
|
+
|
|
1127
|
+
// Platform
|
|
1128
|
+
observations.push(createObservation({
|
|
1129
|
+
agent: this.id,
|
|
1130
|
+
kind: this.domain,
|
|
1131
|
+
severity: 'info',
|
|
1132
|
+
subject: 'probe:system',
|
|
1133
|
+
predicate: 'probe:platform',
|
|
1134
|
+
object: platform(),
|
|
1135
|
+
evidence: {
|
|
1136
|
+
query: 'PLATFORM',
|
|
1137
|
+
result: { platform: platform(), arch: arch() },
|
|
1138
|
+
witnesses: ['os.platform']
|
|
1139
|
+
},
|
|
1140
|
+
metrics: {
|
|
1141
|
+
confidence: 1.0,
|
|
1142
|
+
coverage: 1.0,
|
|
1143
|
+
latency_ms: performance.now() - startTime
|
|
1144
|
+
},
|
|
1145
|
+
tags: ['system', 'platform']
|
|
1146
|
+
}));
|
|
1147
|
+
|
|
1148
|
+
// Architecture
|
|
1149
|
+
observations.push(createObservation({
|
|
1150
|
+
agent: this.id,
|
|
1151
|
+
kind: this.domain,
|
|
1152
|
+
severity: 'info',
|
|
1153
|
+
subject: 'probe:system',
|
|
1154
|
+
predicate: 'probe:architecture',
|
|
1155
|
+
object: arch(),
|
|
1156
|
+
evidence: {
|
|
1157
|
+
query: 'ARCHITECTURE',
|
|
1158
|
+
result: { arch: arch() },
|
|
1159
|
+
witnesses: ['os.arch']
|
|
1160
|
+
},
|
|
1161
|
+
metrics: {
|
|
1162
|
+
confidence: 1.0,
|
|
1163
|
+
coverage: 1.0,
|
|
1164
|
+
latency_ms: performance.now() - startTime
|
|
1165
|
+
},
|
|
1166
|
+
tags: ['system', 'arch']
|
|
1167
|
+
}));
|
|
1168
|
+
|
|
1169
|
+
// Containerized detection
|
|
1170
|
+
let containerized = false;
|
|
1171
|
+
try {
|
|
1172
|
+
containerized = existsSync('/.dockerenv') ||
|
|
1173
|
+
existsSync('/run/.containerenv') ||
|
|
1174
|
+
process.env.KUBERNETES_SERVICE_HOST !== undefined;
|
|
1175
|
+
} catch {
|
|
1176
|
+
containerized = false;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
observations.push(createObservation({
|
|
1180
|
+
agent: this.id,
|
|
1181
|
+
kind: this.domain,
|
|
1182
|
+
severity: 'info',
|
|
1183
|
+
subject: 'probe:system',
|
|
1184
|
+
predicate: 'probe:containerized',
|
|
1185
|
+
object: String(containerized),
|
|
1186
|
+
evidence: {
|
|
1187
|
+
query: 'CONTAINER_CHECK',
|
|
1188
|
+
result: { containerized, checks: ['/.dockerenv', 'KUBERNETES_SERVICE_HOST'] },
|
|
1189
|
+
witnesses: []
|
|
1190
|
+
},
|
|
1191
|
+
metrics: {
|
|
1192
|
+
confidence: 0.85,
|
|
1193
|
+
coverage: 1.0,
|
|
1194
|
+
latency_ms: performance.now() - startTime
|
|
1195
|
+
},
|
|
1196
|
+
tags: ['system', 'container']
|
|
1197
|
+
}));
|
|
1198
|
+
|
|
1199
|
+
// Total memory
|
|
1200
|
+
observations.push(createObservation({
|
|
1201
|
+
agent: this.id,
|
|
1202
|
+
kind: this.domain,
|
|
1203
|
+
severity: 'info',
|
|
1204
|
+
subject: 'probe:system',
|
|
1205
|
+
predicate: 'probe:totalMemory',
|
|
1206
|
+
object: String(totalmem()),
|
|
1207
|
+
evidence: {
|
|
1208
|
+
query: 'TOTAL_MEMORY',
|
|
1209
|
+
result: { bytes: totalmem(), gb: (totalmem() / (1024 ** 3)).toFixed(2) },
|
|
1210
|
+
witnesses: ['os.totalmem']
|
|
1211
|
+
},
|
|
1212
|
+
metrics: {
|
|
1213
|
+
confidence: 1.0,
|
|
1214
|
+
coverage: 1.0,
|
|
1215
|
+
latency_ms: performance.now() - startTime
|
|
1216
|
+
},
|
|
1217
|
+
tags: ['system', 'memory']
|
|
1218
|
+
}));
|
|
1219
|
+
|
|
1220
|
+
return observations;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// ============================================================================
|
|
1225
|
+
// AGENT REGISTRY
|
|
1226
|
+
// ============================================================================
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* AgentRegistry - Manages agent registration and execution
|
|
1230
|
+
* @class AgentRegistry
|
|
1231
|
+
*/
|
|
1232
|
+
export class AgentRegistry {
|
|
1233
|
+
/**
|
|
1234
|
+
*
|
|
1235
|
+
*/
|
|
1236
|
+
constructor() {
|
|
1237
|
+
/** @type {Map<string, Agent>} */
|
|
1238
|
+
this.agents = new Map();
|
|
1239
|
+
this.registerDefault();
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Register default agents (10 domains)
|
|
1244
|
+
* @private
|
|
1245
|
+
*/
|
|
1246
|
+
registerDefault() {
|
|
1247
|
+
this.register('orchestrator', new OrchestratorAgent());
|
|
1248
|
+
this.register('runtime', new RuntimeAgent());
|
|
1249
|
+
this.register('filesystem', new FilesystemAgent());
|
|
1250
|
+
this.register('wasm', new WasmAgent());
|
|
1251
|
+
this.register('performance', new PerformanceAgent());
|
|
1252
|
+
this.register('network', new NetworkAgent());
|
|
1253
|
+
this.register('tooling', new ToolingAgent());
|
|
1254
|
+
this.register('storage', new StorageAgent());
|
|
1255
|
+
this.register('concurrency', new ConcurrencyAgent());
|
|
1256
|
+
this.register('system', new SystemAgent());
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* Register agent
|
|
1261
|
+
* @param {string} id - Agent identifier
|
|
1262
|
+
* @param {Agent} agent - Agent instance
|
|
1263
|
+
*/
|
|
1264
|
+
register(id, agent) {
|
|
1265
|
+
this.agents.set(id, agent);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
/**
|
|
1269
|
+
* Get agent by ID
|
|
1270
|
+
* @param {string} id - Agent identifier
|
|
1271
|
+
* @returns {Agent | undefined}
|
|
1272
|
+
*/
|
|
1273
|
+
get(id) {
|
|
1274
|
+
return this.agents.get(id);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* List all agent IDs
|
|
1279
|
+
* @returns {string[]}
|
|
1280
|
+
*/
|
|
1281
|
+
list() {
|
|
1282
|
+
return Array.from(this.agents.keys());
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Count registered agents
|
|
1287
|
+
* @returns {number}
|
|
1288
|
+
*/
|
|
1289
|
+
count() {
|
|
1290
|
+
return this.agents.size;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* Run all agents in parallel
|
|
1295
|
+
* @param {Object} config - Configuration
|
|
1296
|
+
* @returns {Promise<Array>} All observations from all agents
|
|
1297
|
+
*/
|
|
1298
|
+
async probeAll(config = {}) {
|
|
1299
|
+
const promises = this.list().map(async (id) => {
|
|
1300
|
+
const agent = this.get(id);
|
|
1301
|
+
try {
|
|
1302
|
+
return await agent.probe(config);
|
|
1303
|
+
} catch (err) {
|
|
1304
|
+
console.error(`Agent ${id} failed:`, err.message);
|
|
1305
|
+
return [];
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
const results = await Promise.all(promises);
|
|
1310
|
+
return results.flat();
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// ============================================================================
|
|
1315
|
+
// FACTORY FUNCTIONS
|
|
1316
|
+
// ============================================================================
|
|
1317
|
+
|
|
1318
|
+
/** Create agent registry with all default agents @returns {AgentRegistry} */
|
|
1319
|
+
export function createAgentRegistry() { return new AgentRegistry(); }
|
|
1320
|
+
|
|
1321
|
+
/** Create orchestrator agent @returns {OrchestratorAgent} */
|
|
1322
|
+
export function createOrchestratorAgent() { return new OrchestratorAgent(); }
|
|
1323
|
+
|
|
1324
|
+
/** Create runtime agent @returns {RuntimeAgent} */
|
|
1325
|
+
export function createRuntimeAgent() { return new RuntimeAgent(); }
|
|
1326
|
+
|
|
1327
|
+
/** Create filesystem agent @returns {FilesystemAgent} */
|
|
1328
|
+
export function createFilesystemAgent() { return new FilesystemAgent(); }
|
|
1329
|
+
|
|
1330
|
+
/** Create WASM agent @returns {WasmAgent} */
|
|
1331
|
+
export function createWasmAgent() { return new WasmAgent(); }
|
|
1332
|
+
|
|
1333
|
+
/** Create performance agent @returns {PerformanceAgent} */
|
|
1334
|
+
export function createPerformanceAgent() { return new PerformanceAgent(); }
|
|
1335
|
+
|
|
1336
|
+
/** Create network agent @returns {NetworkAgent} */
|
|
1337
|
+
export function createNetworkAgent() { return new NetworkAgent(); }
|
|
1338
|
+
|
|
1339
|
+
/** Create tooling agent @returns {ToolingAgent} */
|
|
1340
|
+
export function createToolingAgent() { return new ToolingAgent(); }
|
|
1341
|
+
|
|
1342
|
+
/** Create storage agent @returns {StorageAgent} */
|
|
1343
|
+
export function createStorageAgent() { return new StorageAgent(); }
|
|
1344
|
+
|
|
1345
|
+
/** Create concurrency agent @returns {ConcurrencyAgent} */
|
|
1346
|
+
export function createConcurrencyAgent() { return new ConcurrencyAgent(); }
|
|
1347
|
+
|
|
1348
|
+
/** Create system agent @returns {SystemAgent} */
|
|
1349
|
+
export function createSystemAgent() { return new SystemAgent(); }
|
|
1350
|
+
|
|
1351
|
+
// Backward compatibility aliases
|
|
1352
|
+
export const CompletionAgent = OrchestratorAgent;
|
|
1353
|
+
export const ConsistencyAgent = RuntimeAgent;
|
|
1354
|
+
export const ConformanceAgent = FilesystemAgent;
|
|
1355
|
+
export const CoverageAgent = WasmAgent;
|
|
1356
|
+
export const CachingAgent = PerformanceAgent;
|
|
1357
|
+
export const CompletenessAgent = NetworkAgent;
|
|
1358
|
+
export const CoherenceAgent = ToolingAgent;
|
|
1359
|
+
export const ClusteringAgent = StorageAgent;
|
|
1360
|
+
export const ClassificationAgent = ConcurrencyAgent;
|
|
1361
|
+
export const CollaborationAgent = SystemAgent;
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
*
|
|
1365
|
+
*/
|
|
1366
|
+
export function createCompletionAgent() { return new OrchestratorAgent(); }
|
|
1367
|
+
/**
|
|
1368
|
+
*
|
|
1369
|
+
*/
|
|
1370
|
+
export function createConsistencyAgent() { return new RuntimeAgent(); }
|
|
1371
|
+
/**
|
|
1372
|
+
*
|
|
1373
|
+
*/
|
|
1374
|
+
export function createConformanceAgent() { return new FilesystemAgent(); }
|
|
1375
|
+
/**
|
|
1376
|
+
*
|
|
1377
|
+
*/
|
|
1378
|
+
export function createCoverageAgent() { return new WasmAgent(); }
|
|
1379
|
+
/**
|
|
1380
|
+
*
|
|
1381
|
+
*/
|
|
1382
|
+
export function createCachingAgent() { return new PerformanceAgent(); }
|
|
1383
|
+
/**
|
|
1384
|
+
*
|
|
1385
|
+
*/
|
|
1386
|
+
export function createCompletenessAgent() { return new NetworkAgent(); }
|
|
1387
|
+
/**
|
|
1388
|
+
*
|
|
1389
|
+
*/
|
|
1390
|
+
export function createCoherenceAgent() { return new ToolingAgent(); }
|
|
1391
|
+
/**
|
|
1392
|
+
*
|
|
1393
|
+
*/
|
|
1394
|
+
export function createClusteringAgent() { return new StorageAgent(); }
|
|
1395
|
+
/**
|
|
1396
|
+
*
|
|
1397
|
+
*/
|
|
1398
|
+
export function createClassificationAgent() { return new ConcurrencyAgent(); }
|
|
1399
|
+
/**
|
|
1400
|
+
*
|
|
1401
|
+
*/
|
|
1402
|
+
export function createCollaborationAgent() { return new SystemAgent(); }
|