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
@@ -481,4 +481,279 @@ export function _resetState() {
481
481
  _metrics.clear();
482
482
  _violations.clear();
483
483
  _seq = 0;
484
+ _maxActiveSlasPerOrg = DEFAULT_MAX_ACTIVE_SLAS_PER_ORG;
485
+ }
486
+
487
+ /* ═══════════════════════════════════════════════════════════════
488
+ * V2 (Phase 61) — Frozen enums + contract/violation state
489
+ * machines + active-per-org cap + auto-expire + stats-v2.
490
+ * Strictly additive on top of the legacy surface above.
491
+ * ═══════════════════════════════════════════════════════════════ */
492
+
493
+ export const SLA_STATUS_V2 = Object.freeze({
494
+ ACTIVE: "active",
495
+ EXPIRED: "expired",
496
+ TERMINATED: "terminated",
497
+ });
498
+
499
+ export const SLA_TIER_V2 = Object.freeze({
500
+ GOLD: "gold",
501
+ SILVER: "silver",
502
+ BRONZE: "bronze",
503
+ });
504
+
505
+ export const SLA_TERM_V2 = Object.freeze({
506
+ AVAILABILITY: "availability",
507
+ RESPONSE_TIME: "response_time",
508
+ THROUGHPUT: "throughput",
509
+ ERROR_RATE: "error_rate",
510
+ });
511
+
512
+ export const VIOLATION_SEVERITY_V2 = Object.freeze({
513
+ MINOR: "minor",
514
+ MODERATE: "moderate",
515
+ MAJOR: "major",
516
+ CRITICAL: "critical",
517
+ });
518
+
519
+ export const VIOLATION_STATUS_V2 = Object.freeze({
520
+ OPEN: "open",
521
+ ACKNOWLEDGED: "acknowledged",
522
+ RESOLVED: "resolved",
523
+ WAIVED: "waived",
524
+ });
525
+
526
+ const DEFAULT_MAX_ACTIVE_SLAS_PER_ORG = 1;
527
+ let _maxActiveSlasPerOrg = DEFAULT_MAX_ACTIVE_SLAS_PER_ORG;
528
+ export const SLA_DEFAULT_MAX_ACTIVE_PER_ORG = DEFAULT_MAX_ACTIVE_SLAS_PER_ORG;
529
+
530
+ export function setMaxActiveSlasPerOrg(n) {
531
+ if (typeof n !== "number" || !Number.isFinite(n) || n < 1) {
532
+ throw new Error("maxActiveSlasPerOrg must be a positive integer");
533
+ }
534
+ _maxActiveSlasPerOrg = Math.floor(n);
535
+ return _maxActiveSlasPerOrg;
536
+ }
537
+
538
+ export function getMaxActiveSlasPerOrg() {
539
+ return _maxActiveSlasPerOrg;
540
+ }
541
+
542
+ // Contract state machine: active → { expired, terminated }; both terminal.
543
+ const _contractTerminal = new Set([
544
+ SLA_STATUS_V2.EXPIRED,
545
+ SLA_STATUS_V2.TERMINATED,
546
+ ]);
547
+ const _contractAllowed = new Map([
548
+ [
549
+ SLA_STATUS_V2.ACTIVE,
550
+ new Set([SLA_STATUS_V2.EXPIRED, SLA_STATUS_V2.TERMINATED]),
551
+ ],
552
+ [SLA_STATUS_V2.EXPIRED, new Set([])],
553
+ [SLA_STATUS_V2.TERMINATED, new Set([])],
554
+ ]);
555
+
556
+ // Violation state machine: open → { acknowledged, resolved, waived };
557
+ // acknowledged → { resolved, waived }; resolved/waived terminal.
558
+ const _violationTerminal = new Set([
559
+ VIOLATION_STATUS_V2.RESOLVED,
560
+ VIOLATION_STATUS_V2.WAIVED,
561
+ ]);
562
+ const _violationAllowed = new Map([
563
+ [
564
+ VIOLATION_STATUS_V2.OPEN,
565
+ new Set([
566
+ VIOLATION_STATUS_V2.ACKNOWLEDGED,
567
+ VIOLATION_STATUS_V2.RESOLVED,
568
+ VIOLATION_STATUS_V2.WAIVED,
569
+ ]),
570
+ ],
571
+ [
572
+ VIOLATION_STATUS_V2.ACKNOWLEDGED,
573
+ new Set([VIOLATION_STATUS_V2.RESOLVED, VIOLATION_STATUS_V2.WAIVED]),
574
+ ],
575
+ [VIOLATION_STATUS_V2.RESOLVED, new Set([])],
576
+ [VIOLATION_STATUS_V2.WAIVED, new Set([])],
577
+ ]);
578
+
579
+ export function getActiveSlaCountForOrg(orgId) {
580
+ let count = 0;
581
+ for (const c of _contracts.values()) {
582
+ if (c.orgId === orgId && c.status === SLA_STATUS_V2.ACTIVE) count++;
583
+ }
584
+ return count;
585
+ }
586
+
587
+ /**
588
+ * createSLAV2 — like createSLA but enforces per-org active-contract cap
589
+ * and rejects unknown tier/status at the boundary. Augments stored
590
+ * contract with in-memory V2 fields (violationStatus doesn't apply here;
591
+ * see recordViolation*).
592
+ */
593
+ export function createSLAV2(db, config = {}) {
594
+ const orgId = config.orgId;
595
+ if (!orgId) throw new Error("orgId is required");
596
+
597
+ const activeCount = getActiveSlaCountForOrg(orgId);
598
+ if (activeCount >= _maxActiveSlasPerOrg) {
599
+ throw new Error(
600
+ `Max active SLAs per org reached: ${activeCount}/${_maxActiveSlasPerOrg}`,
601
+ );
602
+ }
603
+
604
+ return createSLA(db, config);
605
+ }
606
+
607
+ export function setSLAStatus(db, slaId, newStatus) {
608
+ const contract = _contracts.get(slaId);
609
+ if (!contract) throw new Error(`SLA not found: ${slaId}`);
610
+
611
+ if (!Object.values(SLA_STATUS_V2).includes(newStatus)) {
612
+ throw new Error(`Unknown SLA status: ${newStatus}`);
613
+ }
614
+
615
+ const allowed = _contractAllowed.get(contract.status);
616
+ if (!allowed || !allowed.has(newStatus)) {
617
+ throw new Error(
618
+ `Invalid SLA status transition: ${contract.status} → ${newStatus}`,
619
+ );
620
+ }
621
+
622
+ contract.status = newStatus;
623
+ contract.updatedAt = Date.now();
624
+ db.prepare(
625
+ `UPDATE sla_contracts SET status = ?, updated_at = ? WHERE sla_id = ?`,
626
+ ).run(contract.status, contract.updatedAt, slaId);
627
+
628
+ const { _seq: _omit, ...rest } = contract;
629
+ void _omit;
630
+ return rest;
631
+ }
632
+
633
+ export function expireSLA(db, slaId) {
634
+ return setSLAStatus(db, slaId, SLA_STATUS_V2.EXPIRED);
635
+ }
636
+
637
+ /**
638
+ * autoExpireSLAs — bulk-flip ACTIVE contracts whose endDate < now to
639
+ * EXPIRED. Returns the list of flipped contracts.
640
+ */
641
+ export function autoExpireSLAs(db, nowMs = Date.now()) {
642
+ const flipped = [];
643
+ for (const contract of _contracts.values()) {
644
+ if (contract.status === SLA_STATUS_V2.ACTIVE && contract.endDate < nowMs) {
645
+ contract.status = SLA_STATUS_V2.EXPIRED;
646
+ contract.updatedAt = nowMs;
647
+ db.prepare(
648
+ `UPDATE sla_contracts SET status = ?, updated_at = ? WHERE sla_id = ?`,
649
+ ).run(contract.status, contract.updatedAt, contract.slaId);
650
+ const { _seq: _omit, ...rest } = contract;
651
+ void _omit;
652
+ flipped.push(rest);
653
+ }
654
+ }
655
+ return flipped;
656
+ }
657
+
658
+ export function setViolationStatus(db, violationId, newStatus, patch = {}) {
659
+ const violation = _violations.get(violationId);
660
+ if (!violation) throw new Error(`Violation not found: ${violationId}`);
661
+
662
+ if (!Object.values(VIOLATION_STATUS_V2).includes(newStatus)) {
663
+ throw new Error(`Unknown violation status: ${newStatus}`);
664
+ }
665
+
666
+ const current = violation.v2Status || VIOLATION_STATUS_V2.OPEN;
667
+ const allowed = _violationAllowed.get(current);
668
+ if (!allowed || !allowed.has(newStatus)) {
669
+ throw new Error(
670
+ `Invalid violation status transition: ${current} → ${newStatus}`,
671
+ );
672
+ }
673
+
674
+ violation.v2Status = newStatus;
675
+ if (typeof patch.note === "string") {
676
+ violation.note = patch.note;
677
+ }
678
+ if (_violationTerminal.has(newStatus)) {
679
+ violation.resolvedAt = Date.now();
680
+ db.prepare(
681
+ `UPDATE sla_violations SET resolved_at = ? WHERE violation_id = ?`,
682
+ ).run(violation.resolvedAt, violationId);
683
+ }
684
+
685
+ return { ...violation };
686
+ }
687
+
688
+ export function acknowledgeViolation(db, violationId, note) {
689
+ return setViolationStatus(db, violationId, VIOLATION_STATUS_V2.ACKNOWLEDGED, {
690
+ note,
691
+ });
692
+ }
693
+
694
+ export function resolveViolation(db, violationId, note) {
695
+ return setViolationStatus(db, violationId, VIOLATION_STATUS_V2.RESOLVED, {
696
+ note,
697
+ });
698
+ }
699
+
700
+ export function waiveViolation(db, violationId, note) {
701
+ return setViolationStatus(db, violationId, VIOLATION_STATUS_V2.WAIVED, {
702
+ note,
703
+ });
704
+ }
705
+
706
+ export function getSLAStatsV2() {
707
+ const contracts = [..._contracts.values()];
708
+ const violations = [..._violations.values()];
709
+
710
+ const byStatus = {};
711
+ for (const s of Object.values(SLA_STATUS_V2)) byStatus[s] = 0;
712
+ for (const c of contracts) byStatus[c.status] = (byStatus[c.status] || 0) + 1;
713
+
714
+ const byTier = {};
715
+ for (const t of Object.values(SLA_TIER_V2)) byTier[t] = 0;
716
+ for (const c of contracts) byTier[c.tier] = (byTier[c.tier] || 0) + 1;
717
+
718
+ const bySeverity = {};
719
+ for (const s of Object.values(VIOLATION_SEVERITY_V2)) bySeverity[s] = 0;
720
+ for (const v of violations)
721
+ bySeverity[v.severity] = (bySeverity[v.severity] || 0) + 1;
722
+
723
+ const byTerm = {};
724
+ for (const t of Object.values(SLA_TERM_V2)) byTerm[t] = 0;
725
+ for (const v of violations) byTerm[v.term] = (byTerm[v.term] || 0) + 1;
726
+
727
+ const byViolationStatus = {};
728
+ for (const s of Object.values(VIOLATION_STATUS_V2)) byViolationStatus[s] = 0;
729
+ for (const v of violations) {
730
+ const s = v.v2Status || VIOLATION_STATUS_V2.OPEN;
731
+ byViolationStatus[s] = (byViolationStatus[s] || 0) + 1;
732
+ }
733
+
734
+ let totalCompensation = 0;
735
+ for (const v of violations) {
736
+ if (v.compensationAmount) totalCompensation += v.compensationAmount;
737
+ }
738
+
739
+ const activeOrgs = new Set();
740
+ for (const c of contracts) {
741
+ if (c.status === SLA_STATUS_V2.ACTIVE) activeOrgs.add(c.orgId);
742
+ }
743
+
744
+ return {
745
+ totalContracts: contracts.length,
746
+ activeContracts: byStatus[SLA_STATUS_V2.ACTIVE] || 0,
747
+ activeOrgs: activeOrgs.size,
748
+ maxActiveSlasPerOrg: _maxActiveSlasPerOrg,
749
+ byStatus,
750
+ byTier,
751
+ violations: {
752
+ total: violations.length,
753
+ byTerm,
754
+ bySeverity,
755
+ byStatus: byViolationStatus,
756
+ totalCompensation: Number(totalCompensation.toFixed(4)),
757
+ },
758
+ };
484
759
  }
@@ -281,3 +281,329 @@ export function _resetState() {
281
281
  _posts.clear();
282
282
  _messages.clear();
283
283
  }
284
+
285
+ /* ═══════════════════════════════════════════════════════════════
286
+ * V2 Surface — Social governance layer.
287
+ * Tracks relationship maturity + thread lifecycle independent of
288
+ * legacy contacts/friends/posts/messages stores above.
289
+ * ═══════════════════════════════════════════════════════════════ */
290
+
291
+ export const RELATIONSHIP_MATURITY_V2 = Object.freeze({
292
+ PENDING: "pending",
293
+ CONNECTED: "connected",
294
+ MUTED: "muted",
295
+ BLOCKED: "blocked",
296
+ });
297
+
298
+ export const THREAD_LIFECYCLE_V2 = Object.freeze({
299
+ OPEN: "open",
300
+ ENGAGED: "engaged",
301
+ RESOLVED: "resolved",
302
+ ABANDONED: "abandoned",
303
+ REPORTED: "reported",
304
+ });
305
+
306
+ const REL_TRANSITIONS_V2 = new Map([
307
+ ["pending", new Set(["connected", "blocked"])],
308
+ ["connected", new Set(["muted", "blocked"])],
309
+ ["muted", new Set(["connected", "blocked"])],
310
+ ["blocked", new Set()],
311
+ ]);
312
+ const REL_TERMINALS_V2 = new Set(["blocked"]);
313
+
314
+ const THREAD_TRANSITIONS_V2 = new Map([
315
+ ["open", new Set(["engaged", "abandoned", "reported"])],
316
+ ["engaged", new Set(["resolved", "abandoned", "reported"])],
317
+ ["resolved", new Set()],
318
+ ["abandoned", new Set()],
319
+ ["reported", new Set()],
320
+ ]);
321
+ const THREAD_TERMINALS_V2 = new Set(["resolved", "abandoned", "reported"]);
322
+
323
+ export const SOCIAL_DEFAULT_MAX_CONNECTED_PER_USER = 500;
324
+ export const SOCIAL_DEFAULT_MAX_OPEN_THREADS_PER_USER = 50;
325
+ export const SOCIAL_DEFAULT_RELATIONSHIP_IDLE_MS = 1000 * 60 * 60 * 24 * 180; // 180 days
326
+ export const SOCIAL_DEFAULT_THREAD_STUCK_MS = 1000 * 60 * 60 * 24 * 7; // 7 days
327
+
328
+ const _relationshipsV2 = new Map();
329
+ const _threadsV2 = new Map();
330
+ let _maxConnectedPerUserV2 = SOCIAL_DEFAULT_MAX_CONNECTED_PER_USER;
331
+ let _maxOpenThreadsPerUserV2 = SOCIAL_DEFAULT_MAX_OPEN_THREADS_PER_USER;
332
+ let _relationshipIdleMsV2 = SOCIAL_DEFAULT_RELATIONSHIP_IDLE_MS;
333
+ let _threadStuckMsV2 = SOCIAL_DEFAULT_THREAD_STUCK_MS;
334
+
335
+ function _posIntSocialV2(n, label) {
336
+ const v = Math.floor(Number(n));
337
+ if (!Number.isFinite(v) || v <= 0)
338
+ throw new Error(`${label} must be a positive integer`);
339
+ return v;
340
+ }
341
+
342
+ export function getMaxConnectedPerUserV2() {
343
+ return _maxConnectedPerUserV2;
344
+ }
345
+ export function setMaxConnectedPerUserV2(n) {
346
+ _maxConnectedPerUserV2 = _posIntSocialV2(n, "maxConnectedPerUser");
347
+ }
348
+ export function getMaxOpenThreadsPerUserV2() {
349
+ return _maxOpenThreadsPerUserV2;
350
+ }
351
+ export function setMaxOpenThreadsPerUserV2(n) {
352
+ _maxOpenThreadsPerUserV2 = _posIntSocialV2(n, "maxOpenThreadsPerUser");
353
+ }
354
+ export function getRelationshipIdleMsV2() {
355
+ return _relationshipIdleMsV2;
356
+ }
357
+ export function setRelationshipIdleMsV2(n) {
358
+ _relationshipIdleMsV2 = _posIntSocialV2(n, "relationshipIdleMs");
359
+ }
360
+ export function getThreadStuckMsV2() {
361
+ return _threadStuckMsV2;
362
+ }
363
+ export function setThreadStuckMsV2(n) {
364
+ _threadStuckMsV2 = _posIntSocialV2(n, "threadStuckMs");
365
+ }
366
+
367
+ export function getConnectedCountV2(userId) {
368
+ let n = 0;
369
+ for (const r of _relationshipsV2.values()) {
370
+ if (r.userId === userId && r.status === "connected") n += 1;
371
+ }
372
+ return n;
373
+ }
374
+
375
+ export function getOpenThreadCountV2(userId) {
376
+ let n = 0;
377
+ for (const t of _threadsV2.values()) {
378
+ if (t.userId === userId && (t.status === "open" || t.status === "engaged"))
379
+ n += 1;
380
+ }
381
+ return n;
382
+ }
383
+
384
+ function _copyRelV2(r) {
385
+ return { ...r, metadata: { ...r.metadata } };
386
+ }
387
+ function _copyThreadV2(t) {
388
+ return { ...t, metadata: { ...t.metadata } };
389
+ }
390
+
391
+ export function registerRelationshipV2(
392
+ id,
393
+ { userId, peerId, metadata = {}, now = Date.now() } = {},
394
+ ) {
395
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
396
+ if (!userId || typeof userId !== "string")
397
+ throw new Error("userId must be a string");
398
+ if (!peerId || typeof peerId !== "string")
399
+ throw new Error("peerId must be a string");
400
+ if (_relationshipsV2.has(id))
401
+ throw new Error(`relationship ${id} already exists`);
402
+ const r = {
403
+ id,
404
+ userId,
405
+ peerId,
406
+ status: "pending",
407
+ createdAt: now,
408
+ lastSeenAt: now,
409
+ connectedAt: null,
410
+ blockedAt: null,
411
+ metadata: { ...metadata },
412
+ };
413
+ _relationshipsV2.set(id, r);
414
+ return _copyRelV2(r);
415
+ }
416
+
417
+ export function getRelationshipV2(id) {
418
+ const r = _relationshipsV2.get(id);
419
+ return r ? _copyRelV2(r) : null;
420
+ }
421
+
422
+ export function listRelationshipsV2({ userId, status } = {}) {
423
+ const out = [];
424
+ for (const r of _relationshipsV2.values()) {
425
+ if (userId && r.userId !== userId) continue;
426
+ if (status && r.status !== status) continue;
427
+ out.push(_copyRelV2(r));
428
+ }
429
+ return out;
430
+ }
431
+
432
+ export function setRelationshipStatusV2(id, next, { now = Date.now() } = {}) {
433
+ const r = _relationshipsV2.get(id);
434
+ if (!r) throw new Error(`relationship ${id} not found`);
435
+ if (!REL_TRANSITIONS_V2.has(next))
436
+ throw new Error(`unknown relationship status: ${next}`);
437
+ if (REL_TERMINALS_V2.has(r.status))
438
+ throw new Error(`relationship ${id} is in terminal state ${r.status}`);
439
+ const allowed = REL_TRANSITIONS_V2.get(r.status);
440
+ if (!allowed.has(next))
441
+ throw new Error(
442
+ `cannot transition relationship from ${r.status} to ${next}`,
443
+ );
444
+ if (next === "connected") {
445
+ if (r.status === "pending") {
446
+ const count = getConnectedCountV2(r.userId);
447
+ if (count >= _maxConnectedPerUserV2)
448
+ throw new Error(
449
+ `user ${r.userId} already at connected cap (${_maxConnectedPerUserV2})`,
450
+ );
451
+ }
452
+ if (!r.connectedAt) r.connectedAt = now;
453
+ }
454
+ if (next === "blocked" && !r.blockedAt) r.blockedAt = now;
455
+ r.status = next;
456
+ r.lastSeenAt = now;
457
+ return _copyRelV2(r);
458
+ }
459
+
460
+ export function connectRelationshipV2(id, opts) {
461
+ return setRelationshipStatusV2(id, "connected", opts);
462
+ }
463
+ export function muteRelationshipV2(id, opts) {
464
+ return setRelationshipStatusV2(id, "muted", opts);
465
+ }
466
+ export function blockRelationshipV2(id, opts) {
467
+ return setRelationshipStatusV2(id, "blocked", opts);
468
+ }
469
+
470
+ export function touchRelationshipV2(id, { now = Date.now() } = {}) {
471
+ const r = _relationshipsV2.get(id);
472
+ if (!r) throw new Error(`relationship ${id} not found`);
473
+ r.lastSeenAt = now;
474
+ return _copyRelV2(r);
475
+ }
476
+
477
+ export function createThreadV2(
478
+ id,
479
+ { userId, topic, metadata = {}, now = Date.now() } = {},
480
+ ) {
481
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
482
+ if (!userId || typeof userId !== "string")
483
+ throw new Error("userId must be a string");
484
+ if (!topic || typeof topic !== "string")
485
+ throw new Error("topic must be a string");
486
+ if (_threadsV2.has(id)) throw new Error(`thread ${id} already exists`);
487
+ const count = getOpenThreadCountV2(userId);
488
+ if (count >= _maxOpenThreadsPerUserV2)
489
+ throw new Error(
490
+ `user ${userId} already at open-thread cap (${_maxOpenThreadsPerUserV2})`,
491
+ );
492
+ const t = {
493
+ id,
494
+ userId,
495
+ topic,
496
+ status: "open",
497
+ createdAt: now,
498
+ lastSeenAt: now,
499
+ engagedAt: null,
500
+ settledAt: null,
501
+ metadata: { ...metadata },
502
+ };
503
+ _threadsV2.set(id, t);
504
+ return _copyThreadV2(t);
505
+ }
506
+
507
+ export function getThreadV2(id) {
508
+ const t = _threadsV2.get(id);
509
+ return t ? _copyThreadV2(t) : null;
510
+ }
511
+
512
+ export function listThreadsV2({ userId, status } = {}) {
513
+ const out = [];
514
+ for (const t of _threadsV2.values()) {
515
+ if (userId && t.userId !== userId) continue;
516
+ if (status && t.status !== status) continue;
517
+ out.push(_copyThreadV2(t));
518
+ }
519
+ return out;
520
+ }
521
+
522
+ export function setThreadStatusV2(id, next, { now = Date.now() } = {}) {
523
+ const t = _threadsV2.get(id);
524
+ if (!t) throw new Error(`thread ${id} not found`);
525
+ if (!THREAD_TRANSITIONS_V2.has(next))
526
+ throw new Error(`unknown thread status: ${next}`);
527
+ if (THREAD_TERMINALS_V2.has(t.status))
528
+ throw new Error(`thread ${id} is in terminal state ${t.status}`);
529
+ const allowed = THREAD_TRANSITIONS_V2.get(t.status);
530
+ if (!allowed.has(next))
531
+ throw new Error(`cannot transition thread from ${t.status} to ${next}`);
532
+ if (next === "engaged" && !t.engagedAt) t.engagedAt = now;
533
+ if (THREAD_TERMINALS_V2.has(next) && !t.settledAt) t.settledAt = now;
534
+ t.status = next;
535
+ t.lastSeenAt = now;
536
+ return _copyThreadV2(t);
537
+ }
538
+
539
+ export function engageThreadV2(id, opts) {
540
+ return setThreadStatusV2(id, "engaged", opts);
541
+ }
542
+ export function resolveThreadV2(id, opts) {
543
+ return setThreadStatusV2(id, "resolved", opts);
544
+ }
545
+ export function abandonThreadV2(id, opts) {
546
+ return setThreadStatusV2(id, "abandoned", opts);
547
+ }
548
+ export function reportThreadV2(id, opts) {
549
+ return setThreadStatusV2(id, "reported", opts);
550
+ }
551
+
552
+ export function autoMuteIdleRelationshipsV2({ now = Date.now() } = {}) {
553
+ const flipped = [];
554
+ for (const r of _relationshipsV2.values()) {
555
+ if (r.status !== "connected") continue;
556
+ if (now - r.lastSeenAt > _relationshipIdleMsV2) {
557
+ r.status = "muted";
558
+ r.lastSeenAt = now;
559
+ flipped.push(_copyRelV2(r));
560
+ }
561
+ }
562
+ return flipped;
563
+ }
564
+
565
+ export function autoAbandonStuckThreadsV2({ now = Date.now() } = {}) {
566
+ const flipped = [];
567
+ for (const t of _threadsV2.values()) {
568
+ if (t.status !== "open" && t.status !== "engaged") continue;
569
+ if (now - t.lastSeenAt > _threadStuckMsV2) {
570
+ t.status = "abandoned";
571
+ t.lastSeenAt = now;
572
+ if (!t.settledAt) t.settledAt = now;
573
+ flipped.push(_copyThreadV2(t));
574
+ }
575
+ }
576
+ return flipped;
577
+ }
578
+
579
+ export function getSocialManagerStatsV2() {
580
+ const relationshipsByStatus = {};
581
+ for (const v of Object.values(RELATIONSHIP_MATURITY_V2))
582
+ relationshipsByStatus[v] = 0;
583
+ for (const r of _relationshipsV2.values())
584
+ relationshipsByStatus[r.status] += 1;
585
+
586
+ const threadsByStatus = {};
587
+ for (const v of Object.values(THREAD_LIFECYCLE_V2)) threadsByStatus[v] = 0;
588
+ for (const t of _threadsV2.values()) threadsByStatus[t.status] += 1;
589
+
590
+ return {
591
+ totalRelationshipsV2: _relationshipsV2.size,
592
+ totalThreadsV2: _threadsV2.size,
593
+ maxConnectedPerUser: _maxConnectedPerUserV2,
594
+ maxOpenThreadsPerUser: _maxOpenThreadsPerUserV2,
595
+ relationshipIdleMs: _relationshipIdleMsV2,
596
+ threadStuckMs: _threadStuckMsV2,
597
+ relationshipsByStatus,
598
+ threadsByStatus,
599
+ };
600
+ }
601
+
602
+ export function _resetStateSocialManagerV2() {
603
+ _relationshipsV2.clear();
604
+ _threadsV2.clear();
605
+ _maxConnectedPerUserV2 = SOCIAL_DEFAULT_MAX_CONNECTED_PER_USER;
606
+ _maxOpenThreadsPerUserV2 = SOCIAL_DEFAULT_MAX_OPEN_THREADS_PER_USER;
607
+ _relationshipIdleMsV2 = SOCIAL_DEFAULT_RELATIONSHIP_IDLE_MS;
608
+ _threadStuckMsV2 = SOCIAL_DEFAULT_THREAD_STUCK_MS;
609
+ }