@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.
@@ -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(); }