chainlesschain 0.66.0 → 0.132.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +380 -0
  4. package/src/commands/agent-network.js +254 -1
  5. package/src/commands/audit.js +302 -0
  6. package/src/commands/automation.js +271 -1
  7. package/src/commands/bi.js +348 -0
  8. package/src/commands/codegen.js +224 -0
  9. package/src/commands/collab.js +341 -0
  10. package/src/commands/compliance.js +1035 -0
  11. package/src/commands/cowork.js +221 -0
  12. package/src/commands/crosschain.js +218 -0
  13. package/src/commands/dbevo.js +284 -0
  14. package/src/commands/dev.js +252 -0
  15. package/src/commands/did.js +358 -0
  16. package/src/commands/dlp.js +341 -0
  17. package/src/commands/encrypt.js +341 -0
  18. package/src/commands/evomap.js +394 -0
  19. package/src/commands/export.js +256 -1
  20. package/src/commands/federation.js +283 -0
  21. package/src/commands/fusion.js +258 -0
  22. package/src/commands/governance.js +325 -0
  23. package/src/commands/hardening.js +411 -0
  24. package/src/commands/hook.js +148 -0
  25. package/src/commands/import.js +252 -0
  26. package/src/commands/incentive.js +322 -0
  27. package/src/commands/inference.js +318 -0
  28. package/src/commands/infra.js +244 -0
  29. package/src/commands/instinct.js +260 -0
  30. package/src/commands/ipfs.js +318 -0
  31. package/src/commands/kg.js +387 -0
  32. package/src/commands/llm.js +263 -0
  33. package/src/commands/lowcode.js +356 -0
  34. package/src/commands/marketplace.js +256 -0
  35. package/src/commands/mcp.js +221 -0
  36. package/src/commands/memory.js +248 -0
  37. package/src/commands/multimodal.js +296 -0
  38. package/src/commands/nlprog.js +356 -0
  39. package/src/commands/note.js +244 -0
  40. package/src/commands/ops.js +354 -0
  41. package/src/commands/orchestrate.js +166 -0
  42. package/src/commands/org.js +277 -0
  43. package/src/commands/p2p.js +390 -0
  44. package/src/commands/perception.js +290 -0
  45. package/src/commands/permmem.js +251 -0
  46. package/src/commands/plugin-ecosystem.js +273 -0
  47. package/src/commands/pqc.js +393 -0
  48. package/src/commands/privacy.js +321 -0
  49. package/src/commands/quantization.js +351 -0
  50. package/src/commands/rcache.js +271 -0
  51. package/src/commands/recommend.js +340 -0
  52. package/src/commands/reputation.js +261 -0
  53. package/src/commands/runtime.js +307 -0
  54. package/src/commands/scim.js +262 -0
  55. package/src/commands/session.js +258 -0
  56. package/src/commands/siem.js +246 -0
  57. package/src/commands/skill.js +267 -1
  58. package/src/commands/sla.js +259 -0
  59. package/src/commands/social.js +256 -0
  60. package/src/commands/sso.js +186 -1
  61. package/src/commands/stress.js +230 -0
  62. package/src/commands/sync.js +256 -0
  63. package/src/commands/tech.js +338 -0
  64. package/src/commands/tenant.js +351 -0
  65. package/src/commands/terraform.js +245 -0
  66. package/src/commands/tokens.js +269 -0
  67. package/src/commands/trust.js +249 -0
  68. package/src/commands/wallet.js +277 -0
  69. package/src/commands/workflow.js +171 -0
  70. package/src/commands/zkp.js +335 -0
  71. package/src/index.js +4 -0
  72. package/src/lib/a2a-protocol.js +451 -0
  73. package/src/lib/agent-coordinator.js +325 -0
  74. package/src/lib/agent-network.js +387 -0
  75. package/src/lib/agent-router.js +395 -0
  76. package/src/lib/aiops.js +478 -0
  77. package/src/lib/app-builder.js +239 -0
  78. package/src/lib/audit-logger.js +379 -0
  79. package/src/lib/automation-engine.js +330 -0
  80. package/src/lib/autonomous-developer.js +350 -0
  81. package/src/lib/bi-engine.js +338 -0
  82. package/src/lib/code-agent.js +323 -0
  83. package/src/lib/collaboration-governance.js +364 -0
  84. package/src/lib/community-governance.js +436 -0
  85. package/src/lib/compliance-manager.js +434 -0
  86. package/src/lib/content-recommendation.js +469 -0
  87. package/src/lib/cross-chain.js +345 -0
  88. package/src/lib/crypto-manager.js +350 -0
  89. package/src/lib/dbevo.js +338 -0
  90. package/src/lib/decentral-infra.js +340 -0
  91. package/src/lib/did-manager.js +367 -0
  92. package/src/lib/dlp-engine.js +389 -0
  93. package/src/lib/evomap-federation.js +177 -0
  94. package/src/lib/evomap-governance.js +276 -0
  95. package/src/lib/federation-hardening.js +259 -0
  96. package/src/lib/hardening-manager.js +348 -0
  97. package/src/lib/hook-manager.js +380 -0
  98. package/src/lib/inference-network.js +330 -0
  99. package/src/lib/instinct-manager.js +332 -0
  100. package/src/lib/ipfs-storage.js +334 -0
  101. package/src/lib/knowledge-exporter.js +381 -0
  102. package/src/lib/knowledge-graph.js +432 -0
  103. package/src/lib/knowledge-importer.js +379 -0
  104. package/src/lib/llm-providers.js +391 -0
  105. package/src/lib/mcp-registry.js +333 -0
  106. package/src/lib/memory-manager.js +330 -0
  107. package/src/lib/multimodal.js +346 -0
  108. package/src/lib/nl-programming.js +343 -0
  109. package/src/lib/note-versioning.js +327 -0
  110. package/src/lib/org-manager.js +323 -0
  111. package/src/lib/p2p-manager.js +387 -0
  112. package/src/lib/perception.js +346 -0
  113. package/src/lib/perf-tuning.js +4 -1
  114. package/src/lib/permanent-memory.js +320 -0
  115. package/src/lib/plugin-ecosystem.js +377 -0
  116. package/src/lib/pqc-manager.js +368 -0
  117. package/src/lib/privacy-computing.js +427 -0
  118. package/src/lib/protocol-fusion.js +417 -0
  119. package/src/lib/quantization.js +325 -0
  120. package/src/lib/reputation-optimizer.js +299 -0
  121. package/src/lib/response-cache.js +327 -0
  122. package/src/lib/scim-manager.js +329 -0
  123. package/src/lib/session-manager.js +329 -0
  124. package/src/lib/siem-exporter.js +333 -0
  125. package/src/lib/skill-loader.js +377 -0
  126. package/src/lib/skill-marketplace.js +325 -0
  127. package/src/lib/sla-manager.js +275 -0
  128. package/src/lib/social-manager.js +326 -0
  129. package/src/lib/sso-manager.js +332 -0
  130. package/src/lib/stress-tester.js +330 -0
  131. package/src/lib/sync-manager.js +326 -0
  132. package/src/lib/tech-learning-engine.js +369 -0
  133. package/src/lib/tenant-saas.js +460 -0
  134. package/src/lib/terraform-manager.js +363 -0
  135. package/src/lib/threat-intel.js +335 -0
  136. package/src/lib/token-incentive.js +293 -0
  137. package/src/lib/token-tracker.js +329 -0
  138. package/src/lib/trust-security.js +390 -0
  139. package/src/lib/ueba.js +389 -0
  140. package/src/lib/universal-runtime.js +325 -0
  141. package/src/lib/wallet-manager.js +326 -0
  142. package/src/lib/workflow-engine.js +322 -0
  143. package/src/lib/zkp-engine.js +274 -0
@@ -498,3 +498,349 @@ export function _resetState() {
498
498
  _voiceSessions.clear();
499
499
  _indexEntries.clear();
500
500
  }
501
+
502
+ /* ═════════════════════════════════════════════════════════ *
503
+ * Phase 84 V2 — Sensor Maturity + Capture Lifecycle
504
+ * ═════════════════════════════════════════════════════════ */
505
+
506
+ export const SENSOR_MATURITY_V2 = Object.freeze({
507
+ ONBOARDING: "onboarding",
508
+ ACTIVE: "active",
509
+ DEGRADED: "degraded",
510
+ OFFLINE: "offline",
511
+ RETIRED: "retired",
512
+ });
513
+
514
+ export const CAPTURE_LIFECYCLE_V2 = Object.freeze({
515
+ PENDING: "pending",
516
+ PROCESSING: "processing",
517
+ READY: "ready",
518
+ FAILED: "failed",
519
+ DISCARDED: "discarded",
520
+ });
521
+
522
+ const SENSOR_TRANSITIONS_V2 = new Map([
523
+ ["onboarding", new Set(["active", "retired"])],
524
+ ["active", new Set(["degraded", "offline", "retired"])],
525
+ ["degraded", new Set(["active", "offline", "retired"])],
526
+ ["offline", new Set(["active", "retired"])],
527
+ ]);
528
+ const SENSOR_TERMINALS_V2 = new Set(["retired"]);
529
+
530
+ const CAPTURE_TRANSITIONS_V2 = new Map([
531
+ ["pending", new Set(["processing", "discarded", "failed"])],
532
+ ["processing", new Set(["ready", "failed", "discarded"])],
533
+ ["failed", new Set(["pending", "discarded"])],
534
+ ]);
535
+ const CAPTURE_TERMINALS_V2 = new Set(["ready", "discarded"]);
536
+
537
+ export const PCP_DEFAULT_MAX_ACTIVE_SENSORS_PER_OPERATOR = 25;
538
+ export const PCP_DEFAULT_MAX_PENDING_CAPTURES_PER_SENSOR = 50;
539
+ export const PCP_DEFAULT_SENSOR_IDLE_MS = 30 * 86400000; // 30d
540
+ export const PCP_DEFAULT_CAPTURE_STUCK_MS = 2 * 3600000; // 2h
541
+
542
+ let _maxActiveSensorsPerOperatorV2 =
543
+ PCP_DEFAULT_MAX_ACTIVE_SENSORS_PER_OPERATOR;
544
+ let _maxPendingCapturesPerSensorV2 =
545
+ PCP_DEFAULT_MAX_PENDING_CAPTURES_PER_SENSOR;
546
+ let _sensorIdleMsV2 = PCP_DEFAULT_SENSOR_IDLE_MS;
547
+ let _captureStuckMsV2 = PCP_DEFAULT_CAPTURE_STUCK_MS;
548
+
549
+ function _positiveIntV2(n, label) {
550
+ const v = Math.floor(Number(n));
551
+ if (!Number.isFinite(v) || v <= 0)
552
+ throw new Error(`${label} must be a positive integer`);
553
+ return v;
554
+ }
555
+
556
+ export function getDefaultMaxActiveSensorsPerOperatorV2() {
557
+ return PCP_DEFAULT_MAX_ACTIVE_SENSORS_PER_OPERATOR;
558
+ }
559
+ export function getMaxActiveSensorsPerOperatorV2() {
560
+ return _maxActiveSensorsPerOperatorV2;
561
+ }
562
+ export function setMaxActiveSensorsPerOperatorV2(n) {
563
+ return (_maxActiveSensorsPerOperatorV2 = _positiveIntV2(
564
+ n,
565
+ "maxActiveSensorsPerOperator",
566
+ ));
567
+ }
568
+ export function getDefaultMaxPendingCapturesPerSensorV2() {
569
+ return PCP_DEFAULT_MAX_PENDING_CAPTURES_PER_SENSOR;
570
+ }
571
+ export function getMaxPendingCapturesPerSensorV2() {
572
+ return _maxPendingCapturesPerSensorV2;
573
+ }
574
+ export function setMaxPendingCapturesPerSensorV2(n) {
575
+ return (_maxPendingCapturesPerSensorV2 = _positiveIntV2(
576
+ n,
577
+ "maxPendingCapturesPerSensor",
578
+ ));
579
+ }
580
+ export function getDefaultSensorIdleMsV2() {
581
+ return PCP_DEFAULT_SENSOR_IDLE_MS;
582
+ }
583
+ export function getSensorIdleMsV2() {
584
+ return _sensorIdleMsV2;
585
+ }
586
+ export function setSensorIdleMsV2(ms) {
587
+ return (_sensorIdleMsV2 = _positiveIntV2(ms, "sensorIdleMs"));
588
+ }
589
+ export function getDefaultCaptureStuckMsV2() {
590
+ return PCP_DEFAULT_CAPTURE_STUCK_MS;
591
+ }
592
+ export function getCaptureStuckMsV2() {
593
+ return _captureStuckMsV2;
594
+ }
595
+ export function setCaptureStuckMsV2(ms) {
596
+ return (_captureStuckMsV2 = _positiveIntV2(ms, "captureStuckMs"));
597
+ }
598
+
599
+ const _sensorsV2 = new Map();
600
+ const _capturesV2 = new Map();
601
+
602
+ export function registerSensorV2(
603
+ _db,
604
+ { sensorId, operatorId, modality, initialStatus, metadata } = {},
605
+ ) {
606
+ if (!sensorId) throw new Error("sensorId is required");
607
+ if (!operatorId) throw new Error("operatorId is required");
608
+ if (!modality) throw new Error("modality is required");
609
+ if (!Object.values(MODALITY).includes(modality))
610
+ throw new Error(`Invalid modality: ${modality}`);
611
+ if (_sensorsV2.has(sensorId))
612
+ throw new Error(`Sensor ${sensorId} already exists`);
613
+ const status = initialStatus || SENSOR_MATURITY_V2.ONBOARDING;
614
+ if (!Object.values(SENSOR_MATURITY_V2).includes(status))
615
+ throw new Error(`Invalid initial status: ${status}`);
616
+ if (SENSOR_TERMINALS_V2.has(status))
617
+ throw new Error(`Cannot register in terminal status: ${status}`);
618
+ if (status === SENSOR_MATURITY_V2.ACTIVE) {
619
+ if (getActiveSensorCount(operatorId) >= _maxActiveSensorsPerOperatorV2)
620
+ throw new Error(
621
+ `Operator ${operatorId} reached active-sensor cap (${_maxActiveSensorsPerOperatorV2})`,
622
+ );
623
+ }
624
+ const now = Date.now();
625
+ const record = {
626
+ sensorId,
627
+ operatorId,
628
+ modality,
629
+ status,
630
+ metadata: metadata || {},
631
+ createdAt: now,
632
+ updatedAt: now,
633
+ lastHeartbeatAt: now,
634
+ };
635
+ _sensorsV2.set(sensorId, record);
636
+ return { ...record, metadata: { ...record.metadata } };
637
+ }
638
+
639
+ export function getSensorV2(sensorId) {
640
+ const r = _sensorsV2.get(sensorId);
641
+ return r ? { ...r, metadata: { ...r.metadata } } : null;
642
+ }
643
+
644
+ export function setSensorMaturityV2(_db, sensorId, newStatus, patch = {}) {
645
+ const record = _sensorsV2.get(sensorId);
646
+ if (!record) throw new Error(`Unknown sensor: ${sensorId}`);
647
+ if (!Object.values(SENSOR_MATURITY_V2).includes(newStatus))
648
+ throw new Error(`Invalid status: ${newStatus}`);
649
+ const allowed = SENSOR_TRANSITIONS_V2.get(record.status) || new Set();
650
+ if (!allowed.has(newStatus))
651
+ throw new Error(`Invalid transition: ${record.status} -> ${newStatus}`);
652
+ if (newStatus === SENSOR_MATURITY_V2.ACTIVE) {
653
+ if (
654
+ getActiveSensorCount(record.operatorId) >= _maxActiveSensorsPerOperatorV2
655
+ )
656
+ throw new Error(
657
+ `Operator ${record.operatorId} reached active-sensor cap (${_maxActiveSensorsPerOperatorV2})`,
658
+ );
659
+ }
660
+ record.status = newStatus;
661
+ record.updatedAt = Date.now();
662
+ if (patch.reason !== undefined) record.lastReason = patch.reason;
663
+ if (patch.metadata)
664
+ record.metadata = { ...record.metadata, ...patch.metadata };
665
+ return { ...record, metadata: { ...record.metadata } };
666
+ }
667
+
668
+ export function activateSensor(db, id, reason) {
669
+ return setSensorMaturityV2(db, id, SENSOR_MATURITY_V2.ACTIVE, { reason });
670
+ }
671
+ export function degradeSensor(db, id, reason) {
672
+ return setSensorMaturityV2(db, id, SENSOR_MATURITY_V2.DEGRADED, { reason });
673
+ }
674
+ export function offlineSensor(db, id, reason) {
675
+ return setSensorMaturityV2(db, id, SENSOR_MATURITY_V2.OFFLINE, { reason });
676
+ }
677
+ export function retireSensor(db, id, reason) {
678
+ return setSensorMaturityV2(db, id, SENSOR_MATURITY_V2.RETIRED, { reason });
679
+ }
680
+
681
+ export function touchSensorHeartbeat(sensorId) {
682
+ const record = _sensorsV2.get(sensorId);
683
+ if (!record) throw new Error(`Unknown sensor: ${sensorId}`);
684
+ record.lastHeartbeatAt = Date.now();
685
+ record.updatedAt = record.lastHeartbeatAt;
686
+ return { ...record, metadata: { ...record.metadata } };
687
+ }
688
+
689
+ export function registerCaptureV2(
690
+ _db,
691
+ { captureId, sensorId, initialStatus, metadata } = {},
692
+ ) {
693
+ if (!captureId) throw new Error("captureId is required");
694
+ if (!sensorId) throw new Error("sensorId is required");
695
+ if (!_sensorsV2.has(sensorId)) throw new Error(`Unknown sensor: ${sensorId}`);
696
+ if (_capturesV2.has(captureId))
697
+ throw new Error(`Capture ${captureId} already exists`);
698
+ const status = initialStatus || CAPTURE_LIFECYCLE_V2.PENDING;
699
+ if (!Object.values(CAPTURE_LIFECYCLE_V2).includes(status))
700
+ throw new Error(`Invalid initial status: ${status}`);
701
+ if (CAPTURE_TERMINALS_V2.has(status))
702
+ throw new Error(`Cannot register in terminal status: ${status}`);
703
+ if (status === CAPTURE_LIFECYCLE_V2.PENDING) {
704
+ if (getPendingCaptureCount(sensorId) >= _maxPendingCapturesPerSensorV2)
705
+ throw new Error(
706
+ `Sensor ${sensorId} reached pending-capture cap (${_maxPendingCapturesPerSensorV2})`,
707
+ );
708
+ }
709
+ const now = Date.now();
710
+ const record = {
711
+ captureId,
712
+ sensorId,
713
+ status,
714
+ metadata: metadata || {},
715
+ createdAt: now,
716
+ updatedAt: now,
717
+ };
718
+ _capturesV2.set(captureId, record);
719
+ return { ...record, metadata: { ...record.metadata } };
720
+ }
721
+
722
+ export function getCaptureV2(captureId) {
723
+ const r = _capturesV2.get(captureId);
724
+ return r ? { ...r, metadata: { ...r.metadata } } : null;
725
+ }
726
+
727
+ export function setCaptureStatusV2(_db, captureId, newStatus, patch = {}) {
728
+ const record = _capturesV2.get(captureId);
729
+ if (!record) throw new Error(`Unknown capture: ${captureId}`);
730
+ if (!Object.values(CAPTURE_LIFECYCLE_V2).includes(newStatus))
731
+ throw new Error(`Invalid status: ${newStatus}`);
732
+ const allowed = CAPTURE_TRANSITIONS_V2.get(record.status) || new Set();
733
+ if (!allowed.has(newStatus))
734
+ throw new Error(`Invalid transition: ${record.status} -> ${newStatus}`);
735
+ if (
736
+ newStatus === CAPTURE_LIFECYCLE_V2.PROCESSING &&
737
+ !record.processingStartedAt
738
+ ) {
739
+ record.processingStartedAt = Date.now();
740
+ }
741
+ record.status = newStatus;
742
+ record.updatedAt = Date.now();
743
+ if (patch.reason !== undefined) record.lastReason = patch.reason;
744
+ if (patch.metadata)
745
+ record.metadata = { ...record.metadata, ...patch.metadata };
746
+ return { ...record, metadata: { ...record.metadata } };
747
+ }
748
+
749
+ export function startProcessingCapture(db, id, reason) {
750
+ return setCaptureStatusV2(db, id, CAPTURE_LIFECYCLE_V2.PROCESSING, {
751
+ reason,
752
+ });
753
+ }
754
+ export function markCaptureReady(db, id, reason) {
755
+ return setCaptureStatusV2(db, id, CAPTURE_LIFECYCLE_V2.READY, { reason });
756
+ }
757
+ export function failCapture(db, id, reason) {
758
+ return setCaptureStatusV2(db, id, CAPTURE_LIFECYCLE_V2.FAILED, { reason });
759
+ }
760
+ export function discardCapture(db, id, reason) {
761
+ return setCaptureStatusV2(db, id, CAPTURE_LIFECYCLE_V2.DISCARDED, { reason });
762
+ }
763
+
764
+ export function getActiveSensorCount(operatorId) {
765
+ let n = 0;
766
+ for (const r of _sensorsV2.values()) {
767
+ if (r.status !== SENSOR_MATURITY_V2.ACTIVE) continue;
768
+ if (operatorId && r.operatorId !== operatorId) continue;
769
+ n++;
770
+ }
771
+ return n;
772
+ }
773
+
774
+ export function getPendingCaptureCount(sensorId) {
775
+ let n = 0;
776
+ for (const r of _capturesV2.values()) {
777
+ if (r.status !== CAPTURE_LIFECYCLE_V2.PENDING) continue;
778
+ if (sensorId && r.sensorId !== sensorId) continue;
779
+ n++;
780
+ }
781
+ return n;
782
+ }
783
+
784
+ export function autoOfflineStaleSensors(_db, nowMs) {
785
+ const now = nowMs ?? Date.now();
786
+ const flipped = [];
787
+ for (const r of _sensorsV2.values()) {
788
+ if (
789
+ r.status === SENSOR_MATURITY_V2.ACTIVE ||
790
+ r.status === SENSOR_MATURITY_V2.DEGRADED
791
+ ) {
792
+ if (now - r.lastHeartbeatAt > _sensorIdleMsV2) {
793
+ r.status = SENSOR_MATURITY_V2.OFFLINE;
794
+ r.updatedAt = now;
795
+ r.lastReason = "heartbeat_timeout";
796
+ flipped.push(r.sensorId);
797
+ }
798
+ }
799
+ }
800
+ return { flipped, count: flipped.length };
801
+ }
802
+
803
+ export function autoFailStuckProcessingCaptures(_db, nowMs) {
804
+ const now = nowMs ?? Date.now();
805
+ const flipped = [];
806
+ for (const r of _capturesV2.values()) {
807
+ if (r.status === CAPTURE_LIFECYCLE_V2.PROCESSING) {
808
+ const anchor = r.processingStartedAt || r.createdAt;
809
+ if (now - anchor > _captureStuckMsV2) {
810
+ r.status = CAPTURE_LIFECYCLE_V2.FAILED;
811
+ r.updatedAt = now;
812
+ r.lastReason = "processing_timeout";
813
+ flipped.push(r.captureId);
814
+ }
815
+ }
816
+ }
817
+ return { flipped, count: flipped.length };
818
+ }
819
+
820
+ export function getPerceptionStatsV2() {
821
+ const sensorsByStatus = {};
822
+ for (const s of Object.values(SENSOR_MATURITY_V2)) sensorsByStatus[s] = 0;
823
+ const capturesByStatus = {};
824
+ for (const s of Object.values(CAPTURE_LIFECYCLE_V2)) capturesByStatus[s] = 0;
825
+ for (const r of _sensorsV2.values()) sensorsByStatus[r.status]++;
826
+ for (const r of _capturesV2.values()) capturesByStatus[r.status]++;
827
+ return {
828
+ totalSensorsV2: _sensorsV2.size,
829
+ totalCapturesV2: _capturesV2.size,
830
+ maxActiveSensorsPerOperator: _maxActiveSensorsPerOperatorV2,
831
+ maxPendingCapturesPerSensor: _maxPendingCapturesPerSensorV2,
832
+ sensorIdleMs: _sensorIdleMsV2,
833
+ captureStuckMs: _captureStuckMsV2,
834
+ sensorsByStatus,
835
+ capturesByStatus,
836
+ };
837
+ }
838
+
839
+ export function _resetStateV2() {
840
+ _maxActiveSensorsPerOperatorV2 = PCP_DEFAULT_MAX_ACTIVE_SENSORS_PER_OPERATOR;
841
+ _maxPendingCapturesPerSensorV2 = PCP_DEFAULT_MAX_PENDING_CAPTURES_PER_SENSOR;
842
+ _sensorIdleMsV2 = PCP_DEFAULT_SENSOR_IDLE_MS;
843
+ _captureStuckMsV2 = PCP_DEFAULT_CAPTURE_STUCK_MS;
844
+ _sensorsV2.clear();
845
+ _capturesV2.clear();
846
+ }
@@ -102,8 +102,11 @@ export const BUILTIN_RULES = Object.freeze([
102
102
 
103
103
  /* ── Helpers ────────────────────────────────────────────── */
104
104
 
105
+ let _lastNow = 0;
105
106
  function _now() {
106
- return Date.now();
107
+ const n = Date.now();
108
+ _lastNow = n > _lastNow ? n : _lastNow + 1;
109
+ return _lastNow;
107
110
  }
108
111
 
109
112
  function _strip(row) {
@@ -368,3 +368,323 @@ export class CLIPermanentMemory {
368
368
  }
369
369
  }
370
370
  }
371
+
372
+ /* ═══════════════════════════════════════════════════════════════
373
+ * V2 Surface — Permanent memory governance layer.
374
+ * Tracks per-owner pin maturity + per-pin retention-job lifecycle
375
+ * independent of file/SQLite/BM25 layers.
376
+ * ═══════════════════════════════════════════════════════════════ */
377
+
378
+ export const PIN_MATURITY_V2 = Object.freeze({
379
+ PENDING: "pending",
380
+ ACTIVE: "active",
381
+ DORMANT: "dormant",
382
+ ARCHIVED: "archived",
383
+ });
384
+
385
+ export const RETENTION_JOB_LIFECYCLE_V2 = Object.freeze({
386
+ QUEUED: "queued",
387
+ RUNNING: "running",
388
+ COMPLETED: "completed",
389
+ FAILED: "failed",
390
+ CANCELLED: "cancelled",
391
+ });
392
+
393
+ const PIN_TRANSITIONS_V2 = new Map([
394
+ ["pending", new Set(["active", "archived"])],
395
+ ["active", new Set(["dormant", "archived"])],
396
+ ["dormant", new Set(["active", "archived"])],
397
+ ["archived", new Set()],
398
+ ]);
399
+ const PIN_TERMINALS_V2 = new Set(["archived"]);
400
+
401
+ const JOB_TRANSITIONS_V2 = new Map([
402
+ ["queued", new Set(["running", "cancelled"])],
403
+ ["running", new Set(["completed", "failed", "cancelled"])],
404
+ ["completed", new Set()],
405
+ ["failed", new Set()],
406
+ ["cancelled", new Set()],
407
+ ]);
408
+ const JOB_TERMINALS_V2 = new Set(["completed", "failed", "cancelled"]);
409
+
410
+ export const PERMMEM_DEFAULT_MAX_ACTIVE_PINS_PER_OWNER = 500;
411
+ export const PERMMEM_DEFAULT_MAX_PENDING_JOBS_PER_PIN = 2;
412
+ export const PERMMEM_DEFAULT_PIN_IDLE_MS = 1000 * 60 * 60 * 24 * 90; // 90 days
413
+ export const PERMMEM_DEFAULT_JOB_STUCK_MS = 1000 * 60 * 15; // 15 min
414
+
415
+ const _pinsV2 = new Map();
416
+ const _jobsV2 = new Map();
417
+ let _maxActivePinsPerOwnerV2 = PERMMEM_DEFAULT_MAX_ACTIVE_PINS_PER_OWNER;
418
+ let _maxPendingJobsPerPinV2 = PERMMEM_DEFAULT_MAX_PENDING_JOBS_PER_PIN;
419
+ let _pinIdleMsV2 = PERMMEM_DEFAULT_PIN_IDLE_MS;
420
+ let _jobStuckMsV2 = PERMMEM_DEFAULT_JOB_STUCK_MS;
421
+
422
+ function _posIntPermMemV2(n, label) {
423
+ const v = Math.floor(Number(n));
424
+ if (!Number.isFinite(v) || v <= 0)
425
+ throw new Error(`${label} must be a positive integer`);
426
+ return v;
427
+ }
428
+
429
+ export function getMaxActivePinsPerOwnerV2() {
430
+ return _maxActivePinsPerOwnerV2;
431
+ }
432
+ export function setMaxActivePinsPerOwnerV2(n) {
433
+ _maxActivePinsPerOwnerV2 = _posIntPermMemV2(n, "maxActivePinsPerOwner");
434
+ }
435
+ export function getMaxPendingJobsPerPinV2() {
436
+ return _maxPendingJobsPerPinV2;
437
+ }
438
+ export function setMaxPendingJobsPerPinV2(n) {
439
+ _maxPendingJobsPerPinV2 = _posIntPermMemV2(n, "maxPendingJobsPerPin");
440
+ }
441
+ export function getPinIdleMsV2() {
442
+ return _pinIdleMsV2;
443
+ }
444
+ export function setPinIdleMsV2(n) {
445
+ _pinIdleMsV2 = _posIntPermMemV2(n, "pinIdleMs");
446
+ }
447
+ export function getJobStuckMsV2() {
448
+ return _jobStuckMsV2;
449
+ }
450
+ export function setJobStuckMsV2(n) {
451
+ _jobStuckMsV2 = _posIntPermMemV2(n, "jobStuckMs");
452
+ }
453
+
454
+ export function getActivePinCountV2(ownerId) {
455
+ let n = 0;
456
+ for (const p of _pinsV2.values()) {
457
+ if (p.ownerId === ownerId && p.status === "active") n += 1;
458
+ }
459
+ return n;
460
+ }
461
+
462
+ export function getPendingJobCountV2(pinId) {
463
+ let n = 0;
464
+ for (const j of _jobsV2.values()) {
465
+ if (j.pinId === pinId && (j.status === "queued" || j.status === "running"))
466
+ n += 1;
467
+ }
468
+ return n;
469
+ }
470
+
471
+ function _copyPinV2(p) {
472
+ return { ...p, metadata: { ...p.metadata } };
473
+ }
474
+ function _copyJobV2(j) {
475
+ return { ...j, metadata: { ...j.metadata } };
476
+ }
477
+
478
+ export function registerPinV2(
479
+ id,
480
+ { ownerId, label, metadata = {}, now = Date.now() } = {},
481
+ ) {
482
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
483
+ if (!ownerId || typeof ownerId !== "string")
484
+ throw new Error("ownerId must be a string");
485
+ if (!label || typeof label !== "string")
486
+ throw new Error("label must be a string");
487
+ if (_pinsV2.has(id)) throw new Error(`pin ${id} already exists`);
488
+ const p = {
489
+ id,
490
+ ownerId,
491
+ label,
492
+ status: "pending",
493
+ createdAt: now,
494
+ lastSeenAt: now,
495
+ activatedAt: null,
496
+ archivedAt: null,
497
+ metadata: { ...metadata },
498
+ };
499
+ _pinsV2.set(id, p);
500
+ return _copyPinV2(p);
501
+ }
502
+
503
+ export function getPinV2(id) {
504
+ const p = _pinsV2.get(id);
505
+ return p ? _copyPinV2(p) : null;
506
+ }
507
+
508
+ export function listPinsV2({ ownerId, status } = {}) {
509
+ const out = [];
510
+ for (const p of _pinsV2.values()) {
511
+ if (ownerId && p.ownerId !== ownerId) continue;
512
+ if (status && p.status !== status) continue;
513
+ out.push(_copyPinV2(p));
514
+ }
515
+ return out;
516
+ }
517
+
518
+ export function setPinStatusV2(id, next, { now = Date.now() } = {}) {
519
+ const p = _pinsV2.get(id);
520
+ if (!p) throw new Error(`pin ${id} not found`);
521
+ if (!PIN_TRANSITIONS_V2.has(next))
522
+ throw new Error(`unknown pin status: ${next}`);
523
+ if (PIN_TERMINALS_V2.has(p.status))
524
+ throw new Error(`pin ${id} is in terminal state ${p.status}`);
525
+ const allowed = PIN_TRANSITIONS_V2.get(p.status);
526
+ if (!allowed.has(next))
527
+ throw new Error(`cannot transition pin from ${p.status} to ${next}`);
528
+ if (next === "active") {
529
+ if (p.status === "pending") {
530
+ const count = getActivePinCountV2(p.ownerId);
531
+ if (count >= _maxActivePinsPerOwnerV2)
532
+ throw new Error(
533
+ `owner ${p.ownerId} already at active-pin cap (${_maxActivePinsPerOwnerV2})`,
534
+ );
535
+ }
536
+ if (!p.activatedAt) p.activatedAt = now;
537
+ }
538
+ if (next === "archived" && !p.archivedAt) p.archivedAt = now;
539
+ p.status = next;
540
+ p.lastSeenAt = now;
541
+ return _copyPinV2(p);
542
+ }
543
+
544
+ export function activatePinV2(id, opts) {
545
+ return setPinStatusV2(id, "active", opts);
546
+ }
547
+ export function dormantPinV2(id, opts) {
548
+ return setPinStatusV2(id, "dormant", opts);
549
+ }
550
+ export function archivePinV2(id, opts) {
551
+ return setPinStatusV2(id, "archived", opts);
552
+ }
553
+
554
+ export function touchPinV2(id, { now = Date.now() } = {}) {
555
+ const p = _pinsV2.get(id);
556
+ if (!p) throw new Error(`pin ${id} not found`);
557
+ p.lastSeenAt = now;
558
+ return _copyPinV2(p);
559
+ }
560
+
561
+ export function createRetentionJobV2(
562
+ id,
563
+ { pinId, kind = "review", metadata = {}, now = Date.now() } = {},
564
+ ) {
565
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
566
+ if (!pinId || typeof pinId !== "string")
567
+ throw new Error("pinId must be a string");
568
+ if (_jobsV2.has(id)) throw new Error(`job ${id} already exists`);
569
+ const count = getPendingJobCountV2(pinId);
570
+ if (count >= _maxPendingJobsPerPinV2)
571
+ throw new Error(
572
+ `pin ${pinId} already at pending-job cap (${_maxPendingJobsPerPinV2})`,
573
+ );
574
+ const j = {
575
+ id,
576
+ pinId,
577
+ kind,
578
+ status: "queued",
579
+ createdAt: now,
580
+ lastSeenAt: now,
581
+ startedAt: null,
582
+ settledAt: null,
583
+ metadata: { ...metadata },
584
+ };
585
+ _jobsV2.set(id, j);
586
+ return _copyJobV2(j);
587
+ }
588
+
589
+ export function getRetentionJobV2(id) {
590
+ const j = _jobsV2.get(id);
591
+ return j ? _copyJobV2(j) : null;
592
+ }
593
+
594
+ export function listRetentionJobsV2({ pinId, status } = {}) {
595
+ const out = [];
596
+ for (const j of _jobsV2.values()) {
597
+ if (pinId && j.pinId !== pinId) continue;
598
+ if (status && j.status !== status) continue;
599
+ out.push(_copyJobV2(j));
600
+ }
601
+ return out;
602
+ }
603
+
604
+ export function setRetentionJobStatusV2(id, next, { now = Date.now() } = {}) {
605
+ const j = _jobsV2.get(id);
606
+ if (!j) throw new Error(`job ${id} not found`);
607
+ if (!JOB_TRANSITIONS_V2.has(next))
608
+ throw new Error(`unknown job status: ${next}`);
609
+ if (JOB_TERMINALS_V2.has(j.status))
610
+ throw new Error(`job ${id} is in terminal state ${j.status}`);
611
+ const allowed = JOB_TRANSITIONS_V2.get(j.status);
612
+ if (!allowed.has(next))
613
+ throw new Error(`cannot transition job from ${j.status} to ${next}`);
614
+ if (next === "running" && !j.startedAt) j.startedAt = now;
615
+ if (JOB_TERMINALS_V2.has(next) && !j.settledAt) j.settledAt = now;
616
+ j.status = next;
617
+ j.lastSeenAt = now;
618
+ return _copyJobV2(j);
619
+ }
620
+
621
+ export function startRetentionJobV2(id, opts) {
622
+ return setRetentionJobStatusV2(id, "running", opts);
623
+ }
624
+ export function completeRetentionJobV2(id, opts) {
625
+ return setRetentionJobStatusV2(id, "completed", opts);
626
+ }
627
+ export function failRetentionJobV2(id, opts) {
628
+ return setRetentionJobStatusV2(id, "failed", opts);
629
+ }
630
+ export function cancelRetentionJobV2(id, opts) {
631
+ return setRetentionJobStatusV2(id, "cancelled", opts);
632
+ }
633
+
634
+ export function autoDormantIdlePinsV2({ now = Date.now() } = {}) {
635
+ const flipped = [];
636
+ for (const p of _pinsV2.values()) {
637
+ if (p.status !== "active") continue;
638
+ if (now - p.lastSeenAt > _pinIdleMsV2) {
639
+ p.status = "dormant";
640
+ p.lastSeenAt = now;
641
+ flipped.push(_copyPinV2(p));
642
+ }
643
+ }
644
+ return flipped;
645
+ }
646
+
647
+ export function autoFailStuckJobsV2({ now = Date.now() } = {}) {
648
+ const flipped = [];
649
+ for (const j of _jobsV2.values()) {
650
+ if (j.status !== "running") continue;
651
+ if (now - j.lastSeenAt > _jobStuckMsV2) {
652
+ j.status = "failed";
653
+ j.lastSeenAt = now;
654
+ if (!j.settledAt) j.settledAt = now;
655
+ flipped.push(_copyJobV2(j));
656
+ }
657
+ }
658
+ return flipped;
659
+ }
660
+
661
+ export function getPermanentMemoryStatsV2() {
662
+ const pinsByStatus = {};
663
+ for (const v of Object.values(PIN_MATURITY_V2)) pinsByStatus[v] = 0;
664
+ for (const p of _pinsV2.values()) pinsByStatus[p.status] += 1;
665
+
666
+ const jobsByStatus = {};
667
+ for (const v of Object.values(RETENTION_JOB_LIFECYCLE_V2))
668
+ jobsByStatus[v] = 0;
669
+ for (const j of _jobsV2.values()) jobsByStatus[j.status] += 1;
670
+
671
+ return {
672
+ totalPinsV2: _pinsV2.size,
673
+ totalJobsV2: _jobsV2.size,
674
+ maxActivePinsPerOwner: _maxActivePinsPerOwnerV2,
675
+ maxPendingJobsPerPin: _maxPendingJobsPerPinV2,
676
+ pinIdleMs: _pinIdleMsV2,
677
+ jobStuckMs: _jobStuckMsV2,
678
+ pinsByStatus,
679
+ jobsByStatus,
680
+ };
681
+ }
682
+
683
+ export function _resetStatePermanentMemoryV2() {
684
+ _pinsV2.clear();
685
+ _jobsV2.clear();
686
+ _maxActivePinsPerOwnerV2 = PERMMEM_DEFAULT_MAX_ACTIVE_PINS_PER_OWNER;
687
+ _maxPendingJobsPerPinV2 = PERMMEM_DEFAULT_MAX_PENDING_JOBS_PER_PIN;
688
+ _pinIdleMsV2 = PERMMEM_DEFAULT_PIN_IDLE_MS;
689
+ _jobStuckMsV2 = PERMMEM_DEFAULT_JOB_STUCK_MS;
690
+ }