chainlesschain 0.81.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 (110) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/agent-network.js +254 -1
  4. package/src/commands/audit.js +302 -0
  5. package/src/commands/automation.js +271 -1
  6. package/src/commands/codegen.js +224 -0
  7. package/src/commands/collab.js +341 -0
  8. package/src/commands/compliance.js +1035 -0
  9. package/src/commands/cowork.js +221 -0
  10. package/src/commands/dbevo.js +284 -0
  11. package/src/commands/dev.js +252 -0
  12. package/src/commands/did.js +358 -0
  13. package/src/commands/encrypt.js +341 -0
  14. package/src/commands/export.js +256 -1
  15. package/src/commands/fusion.js +258 -0
  16. package/src/commands/governance.js +325 -0
  17. package/src/commands/hardening.js +411 -0
  18. package/src/commands/hook.js +148 -0
  19. package/src/commands/import.js +252 -0
  20. package/src/commands/incentive.js +322 -0
  21. package/src/commands/infra.js +244 -0
  22. package/src/commands/instinct.js +260 -0
  23. package/src/commands/ipfs.js +318 -0
  24. package/src/commands/kg.js +387 -0
  25. package/src/commands/llm.js +263 -0
  26. package/src/commands/mcp.js +221 -0
  27. package/src/commands/memory.js +248 -0
  28. package/src/commands/multimodal.js +296 -0
  29. package/src/commands/nlprog.js +356 -0
  30. package/src/commands/note.js +244 -0
  31. package/src/commands/ops.js +354 -0
  32. package/src/commands/orchestrate.js +166 -0
  33. package/src/commands/org.js +277 -0
  34. package/src/commands/p2p.js +390 -0
  35. package/src/commands/perception.js +290 -0
  36. package/src/commands/permmem.js +251 -0
  37. package/src/commands/plugin-ecosystem.js +273 -0
  38. package/src/commands/pqc.js +393 -0
  39. package/src/commands/quantization.js +351 -0
  40. package/src/commands/rcache.js +271 -0
  41. package/src/commands/recommend.js +340 -0
  42. package/src/commands/runtime.js +307 -0
  43. package/src/commands/scim.js +262 -0
  44. package/src/commands/session.js +258 -0
  45. package/src/commands/skill.js +267 -1
  46. package/src/commands/social.js +256 -0
  47. package/src/commands/sso.js +186 -1
  48. package/src/commands/sync.js +256 -0
  49. package/src/commands/tech.js +338 -0
  50. package/src/commands/tenant.js +351 -0
  51. package/src/commands/tokens.js +269 -0
  52. package/src/commands/trust.js +249 -0
  53. package/src/commands/wallet.js +277 -0
  54. package/src/commands/workflow.js +171 -0
  55. package/src/index.js +4 -0
  56. package/src/lib/agent-coordinator.js +325 -0
  57. package/src/lib/agent-network.js +387 -0
  58. package/src/lib/agent-router.js +395 -0
  59. package/src/lib/aiops.js +478 -0
  60. package/src/lib/audit-logger.js +379 -0
  61. package/src/lib/automation-engine.js +330 -0
  62. package/src/lib/autonomous-developer.js +350 -0
  63. package/src/lib/code-agent.js +323 -0
  64. package/src/lib/collaboration-governance.js +364 -0
  65. package/src/lib/community-governance.js +436 -0
  66. package/src/lib/compliance-manager.js +434 -0
  67. package/src/lib/content-recommendation.js +469 -0
  68. package/src/lib/crypto-manager.js +350 -0
  69. package/src/lib/dbevo.js +338 -0
  70. package/src/lib/decentral-infra.js +340 -0
  71. package/src/lib/did-manager.js +367 -0
  72. package/src/lib/hardening-manager.js +348 -0
  73. package/src/lib/hook-manager.js +380 -0
  74. package/src/lib/instinct-manager.js +332 -0
  75. package/src/lib/ipfs-storage.js +334 -0
  76. package/src/lib/knowledge-exporter.js +381 -0
  77. package/src/lib/knowledge-graph.js +432 -0
  78. package/src/lib/knowledge-importer.js +379 -0
  79. package/src/lib/llm-providers.js +391 -0
  80. package/src/lib/mcp-registry.js +333 -0
  81. package/src/lib/memory-manager.js +330 -0
  82. package/src/lib/multimodal.js +346 -0
  83. package/src/lib/nl-programming.js +343 -0
  84. package/src/lib/note-versioning.js +327 -0
  85. package/src/lib/org-manager.js +323 -0
  86. package/src/lib/p2p-manager.js +387 -0
  87. package/src/lib/perception.js +346 -0
  88. package/src/lib/perf-tuning.js +4 -1
  89. package/src/lib/permanent-memory.js +320 -0
  90. package/src/lib/plugin-ecosystem.js +377 -0
  91. package/src/lib/pqc-manager.js +368 -0
  92. package/src/lib/protocol-fusion.js +417 -0
  93. package/src/lib/quantization.js +325 -0
  94. package/src/lib/response-cache.js +327 -0
  95. package/src/lib/scim-manager.js +329 -0
  96. package/src/lib/session-manager.js +329 -0
  97. package/src/lib/skill-loader.js +377 -0
  98. package/src/lib/social-manager.js +326 -0
  99. package/src/lib/sso-manager.js +332 -0
  100. package/src/lib/sync-manager.js +326 -0
  101. package/src/lib/tech-learning-engine.js +369 -0
  102. package/src/lib/tenant-saas.js +460 -0
  103. package/src/lib/threat-intel.js +335 -0
  104. package/src/lib/token-incentive.js +293 -0
  105. package/src/lib/token-tracker.js +329 -0
  106. package/src/lib/trust-security.js +390 -0
  107. package/src/lib/ueba.js +389 -0
  108. package/src/lib/universal-runtime.js +325 -0
  109. package/src/lib/wallet-manager.js +326 -0
  110. package/src/lib/workflow-engine.js +322 -0
@@ -356,3 +356,380 @@ export class CLISkillLoader {
356
356
  this._cache = null;
357
357
  }
358
358
  }
359
+
360
+ // ─── V2 Governance Layer ────────────────────────────────────────────
361
+ //
362
+ // In-memory governance for skill registrations + execution tickets,
363
+ // independent of the file-based 4-layer CLISkillLoader (which scans
364
+ // bundled/marketplace/managed/workspace dirs). V2 tracks maturity
365
+ // transitions, per-owner active-skill caps, per-skill pending-execution
366
+ // caps, stamp-once timestamps, and bulk auto-flip routines.
367
+
368
+ export const SKILL_MATURITY_V2 = Object.freeze({
369
+ PENDING: "pending",
370
+ ACTIVE: "active",
371
+ DEPRECATED: "deprecated",
372
+ ARCHIVED: "archived",
373
+ });
374
+
375
+ export const EXECUTION_LIFECYCLE_V2 = Object.freeze({
376
+ QUEUED: "queued",
377
+ RUNNING: "running",
378
+ SUCCEEDED: "succeeded",
379
+ FAILED: "failed",
380
+ CANCELLED: "cancelled",
381
+ });
382
+
383
+ const _SKILL_TRANSITIONS_V2 = new Map([
384
+ [
385
+ SKILL_MATURITY_V2.PENDING,
386
+ new Set([SKILL_MATURITY_V2.ACTIVE, SKILL_MATURITY_V2.ARCHIVED]),
387
+ ],
388
+ [
389
+ SKILL_MATURITY_V2.ACTIVE,
390
+ new Set([SKILL_MATURITY_V2.DEPRECATED, SKILL_MATURITY_V2.ARCHIVED]),
391
+ ],
392
+ [
393
+ SKILL_MATURITY_V2.DEPRECATED,
394
+ new Set([SKILL_MATURITY_V2.ACTIVE, SKILL_MATURITY_V2.ARCHIVED]),
395
+ ],
396
+ [SKILL_MATURITY_V2.ARCHIVED, new Set()],
397
+ ]);
398
+
399
+ const _SKILL_TERMINALS_V2 = new Set([SKILL_MATURITY_V2.ARCHIVED]);
400
+
401
+ const _EXEC_TRANSITIONS_V2 = new Map([
402
+ [
403
+ EXECUTION_LIFECYCLE_V2.QUEUED,
404
+ new Set([EXECUTION_LIFECYCLE_V2.RUNNING, EXECUTION_LIFECYCLE_V2.CANCELLED]),
405
+ ],
406
+ [
407
+ EXECUTION_LIFECYCLE_V2.RUNNING,
408
+ new Set([
409
+ EXECUTION_LIFECYCLE_V2.SUCCEEDED,
410
+ EXECUTION_LIFECYCLE_V2.FAILED,
411
+ EXECUTION_LIFECYCLE_V2.CANCELLED,
412
+ ]),
413
+ ],
414
+ [EXECUTION_LIFECYCLE_V2.SUCCEEDED, new Set()],
415
+ [EXECUTION_LIFECYCLE_V2.FAILED, new Set()],
416
+ [EXECUTION_LIFECYCLE_V2.CANCELLED, new Set()],
417
+ ]);
418
+
419
+ const _EXEC_TERMINALS_V2 = new Set([
420
+ EXECUTION_LIFECYCLE_V2.SUCCEEDED,
421
+ EXECUTION_LIFECYCLE_V2.FAILED,
422
+ EXECUTION_LIFECYCLE_V2.CANCELLED,
423
+ ]);
424
+
425
+ export const SKILL_DEFAULT_MAX_ACTIVE_PER_OWNER = 30;
426
+ export const SKILL_DEFAULT_MAX_PENDING_EXECUTIONS_PER_SKILL = 5;
427
+ export const SKILL_DEFAULT_SKILL_IDLE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
428
+ export const SKILL_DEFAULT_EXEC_STUCK_MS = 15 * 60 * 1000; // 15 min
429
+
430
+ const _stateV2 = {
431
+ skills: new Map(),
432
+ executions: new Map(),
433
+ maxActiveSkillsPerOwner: SKILL_DEFAULT_MAX_ACTIVE_PER_OWNER,
434
+ maxPendingExecutionsPerSkill: SKILL_DEFAULT_MAX_PENDING_EXECUTIONS_PER_SKILL,
435
+ skillIdleMs: SKILL_DEFAULT_SKILL_IDLE_MS,
436
+ execStuckMs: SKILL_DEFAULT_EXEC_STUCK_MS,
437
+ };
438
+
439
+ function _posIntSkillV2(n, label) {
440
+ const v = Math.floor(Number(n));
441
+ if (!Number.isFinite(v) || v <= 0) {
442
+ throw new Error(`${label} must be a positive integer, got ${n}`);
443
+ }
444
+ return v;
445
+ }
446
+
447
+ export function getMaxActiveSkillsPerOwnerV2() {
448
+ return _stateV2.maxActiveSkillsPerOwner;
449
+ }
450
+
451
+ export function setMaxActiveSkillsPerOwnerV2(n) {
452
+ _stateV2.maxActiveSkillsPerOwner = _posIntSkillV2(
453
+ n,
454
+ "maxActiveSkillsPerOwner",
455
+ );
456
+ }
457
+
458
+ export function getMaxPendingExecutionsPerSkillV2() {
459
+ return _stateV2.maxPendingExecutionsPerSkill;
460
+ }
461
+
462
+ export function setMaxPendingExecutionsPerSkillV2(n) {
463
+ _stateV2.maxPendingExecutionsPerSkill = _posIntSkillV2(
464
+ n,
465
+ "maxPendingExecutionsPerSkill",
466
+ );
467
+ }
468
+
469
+ export function getSkillIdleMsV2() {
470
+ return _stateV2.skillIdleMs;
471
+ }
472
+
473
+ export function setSkillIdleMsV2(ms) {
474
+ _stateV2.skillIdleMs = _posIntSkillV2(ms, "skillIdleMs");
475
+ }
476
+
477
+ export function getExecStuckMsV2() {
478
+ return _stateV2.execStuckMs;
479
+ }
480
+
481
+ export function setExecStuckMsV2(ms) {
482
+ _stateV2.execStuckMs = _posIntSkillV2(ms, "execStuckMs");
483
+ }
484
+
485
+ function _copySkillV2(s) {
486
+ return { ...s, metadata: { ...s.metadata } };
487
+ }
488
+
489
+ function _copyExecV2(e) {
490
+ return { ...e, metadata: { ...e.metadata } };
491
+ }
492
+
493
+ export function getActiveSkillCountV2(ownerId) {
494
+ let count = 0;
495
+ for (const s of _stateV2.skills.values()) {
496
+ if (s.ownerId === ownerId && s.status === SKILL_MATURITY_V2.ACTIVE) count++;
497
+ }
498
+ return count;
499
+ }
500
+
501
+ export function getPendingExecutionCountV2(skillId) {
502
+ let count = 0;
503
+ for (const e of _stateV2.executions.values()) {
504
+ if (
505
+ e.skillId === skillId &&
506
+ (e.status === EXECUTION_LIFECYCLE_V2.QUEUED ||
507
+ e.status === EXECUTION_LIFECYCLE_V2.RUNNING)
508
+ ) {
509
+ count++;
510
+ }
511
+ }
512
+ return count;
513
+ }
514
+
515
+ export function registerSkillV2(id, { ownerId, name, layer, metadata } = {}) {
516
+ if (!id) throw new Error("skill id is required");
517
+ if (!ownerId) throw new Error("ownerId is required");
518
+ if (!name) throw new Error("name is required");
519
+ if (_stateV2.skills.has(id)) throw new Error(`skill ${id} already exists`);
520
+ const now = Date.now();
521
+ const skill = {
522
+ id,
523
+ ownerId,
524
+ name,
525
+ layer: layer || "workspace",
526
+ status: SKILL_MATURITY_V2.PENDING,
527
+ createdAt: now,
528
+ lastSeenAt: now,
529
+ activatedAt: null,
530
+ archivedAt: null,
531
+ metadata: metadata ? { ...metadata } : {},
532
+ };
533
+ _stateV2.skills.set(id, skill);
534
+ return _copySkillV2(skill);
535
+ }
536
+
537
+ export function getSkillV2(id) {
538
+ const s = _stateV2.skills.get(id);
539
+ return s ? _copySkillV2(s) : null;
540
+ }
541
+
542
+ export function listSkillsV2({ ownerId, status, layer } = {}) {
543
+ const out = [];
544
+ for (const s of _stateV2.skills.values()) {
545
+ if (ownerId && s.ownerId !== ownerId) continue;
546
+ if (status && s.status !== status) continue;
547
+ if (layer && s.layer !== layer) continue;
548
+ out.push(_copySkillV2(s));
549
+ }
550
+ return out;
551
+ }
552
+
553
+ export function setSkillStatusV2(id, next) {
554
+ const s = _stateV2.skills.get(id);
555
+ if (!s) throw new Error(`skill ${id} not found`);
556
+ const allowed = _SKILL_TRANSITIONS_V2.get(s.status);
557
+ if (!allowed || !allowed.has(next)) {
558
+ throw new Error(`invalid skill transition: ${s.status} → ${next}`);
559
+ }
560
+ if (
561
+ s.status === SKILL_MATURITY_V2.PENDING &&
562
+ next === SKILL_MATURITY_V2.ACTIVE
563
+ ) {
564
+ const count = getActiveSkillCountV2(s.ownerId);
565
+ if (count >= _stateV2.maxActiveSkillsPerOwner) {
566
+ throw new Error(
567
+ `owner ${s.ownerId} active-skill cap reached (${count}/${_stateV2.maxActiveSkillsPerOwner})`,
568
+ );
569
+ }
570
+ }
571
+ const now = Date.now();
572
+ s.status = next;
573
+ s.lastSeenAt = now;
574
+ if (next === SKILL_MATURITY_V2.ACTIVE && !s.activatedAt) s.activatedAt = now;
575
+ if (_SKILL_TERMINALS_V2.has(next) && !s.archivedAt) s.archivedAt = now;
576
+ return _copySkillV2(s);
577
+ }
578
+
579
+ export function activateSkillV2(id) {
580
+ return setSkillStatusV2(id, SKILL_MATURITY_V2.ACTIVE);
581
+ }
582
+
583
+ export function deprecateSkillV2(id) {
584
+ return setSkillStatusV2(id, SKILL_MATURITY_V2.DEPRECATED);
585
+ }
586
+
587
+ export function archiveSkillV2(id) {
588
+ return setSkillStatusV2(id, SKILL_MATURITY_V2.ARCHIVED);
589
+ }
590
+
591
+ export function touchSkillV2(id) {
592
+ const s = _stateV2.skills.get(id);
593
+ if (!s) throw new Error(`skill ${id} not found`);
594
+ s.lastSeenAt = Date.now();
595
+ return _copySkillV2(s);
596
+ }
597
+
598
+ export function createExecutionV2(id, { skillId, kind, metadata } = {}) {
599
+ if (!id) throw new Error("execution id is required");
600
+ if (!skillId) throw new Error("skillId is required");
601
+ if (_stateV2.executions.has(id))
602
+ throw new Error(`execution ${id} already exists`);
603
+ const skill = _stateV2.skills.get(skillId);
604
+ if (!skill) throw new Error(`skill ${skillId} not found`);
605
+ const pending = getPendingExecutionCountV2(skillId);
606
+ if (pending >= _stateV2.maxPendingExecutionsPerSkill) {
607
+ throw new Error(
608
+ `skill ${skillId} pending-execution cap reached (${pending}/${_stateV2.maxPendingExecutionsPerSkill})`,
609
+ );
610
+ }
611
+ const now = Date.now();
612
+ const exec = {
613
+ id,
614
+ skillId,
615
+ kind: kind || "invoke",
616
+ status: EXECUTION_LIFECYCLE_V2.QUEUED,
617
+ createdAt: now,
618
+ lastSeenAt: now,
619
+ startedAt: null,
620
+ settledAt: null,
621
+ metadata: metadata ? { ...metadata } : {},
622
+ };
623
+ _stateV2.executions.set(id, exec);
624
+ return _copyExecV2(exec);
625
+ }
626
+
627
+ export function getExecutionV2(id) {
628
+ const e = _stateV2.executions.get(id);
629
+ return e ? _copyExecV2(e) : null;
630
+ }
631
+
632
+ export function listExecutionsV2({ skillId, status } = {}) {
633
+ const out = [];
634
+ for (const e of _stateV2.executions.values()) {
635
+ if (skillId && e.skillId !== skillId) continue;
636
+ if (status && e.status !== status) continue;
637
+ out.push(_copyExecV2(e));
638
+ }
639
+ return out;
640
+ }
641
+
642
+ export function setExecutionStatusV2(id, next) {
643
+ const e = _stateV2.executions.get(id);
644
+ if (!e) throw new Error(`execution ${id} not found`);
645
+ const allowed = _EXEC_TRANSITIONS_V2.get(e.status);
646
+ if (!allowed || !allowed.has(next)) {
647
+ throw new Error(`invalid execution transition: ${e.status} → ${next}`);
648
+ }
649
+ const now = Date.now();
650
+ e.status = next;
651
+ e.lastSeenAt = now;
652
+ if (next === EXECUTION_LIFECYCLE_V2.RUNNING && !e.startedAt)
653
+ e.startedAt = now;
654
+ if (_EXEC_TERMINALS_V2.has(next) && !e.settledAt) e.settledAt = now;
655
+ return _copyExecV2(e);
656
+ }
657
+
658
+ export function startExecutionV2(id) {
659
+ return setExecutionStatusV2(id, EXECUTION_LIFECYCLE_V2.RUNNING);
660
+ }
661
+
662
+ export function succeedExecutionV2(id) {
663
+ return setExecutionStatusV2(id, EXECUTION_LIFECYCLE_V2.SUCCEEDED);
664
+ }
665
+
666
+ export function failExecutionV2(id) {
667
+ return setExecutionStatusV2(id, EXECUTION_LIFECYCLE_V2.FAILED);
668
+ }
669
+
670
+ export function cancelExecutionV2(id) {
671
+ return setExecutionStatusV2(id, EXECUTION_LIFECYCLE_V2.CANCELLED);
672
+ }
673
+
674
+ export function autoDeprecateIdleSkillsV2({ now = Date.now() } = {}) {
675
+ const flipped = [];
676
+ for (const s of _stateV2.skills.values()) {
677
+ if (
678
+ s.status === SKILL_MATURITY_V2.ACTIVE &&
679
+ now - s.lastSeenAt > _stateV2.skillIdleMs
680
+ ) {
681
+ s.status = SKILL_MATURITY_V2.DEPRECATED;
682
+ s.lastSeenAt = now;
683
+ flipped.push(_copySkillV2(s));
684
+ }
685
+ }
686
+ return flipped;
687
+ }
688
+
689
+ export function autoFailStuckExecutionsV2({ now = Date.now() } = {}) {
690
+ const flipped = [];
691
+ for (const e of _stateV2.executions.values()) {
692
+ if (
693
+ e.status === EXECUTION_LIFECYCLE_V2.RUNNING &&
694
+ now - e.lastSeenAt > _stateV2.execStuckMs
695
+ ) {
696
+ e.status = EXECUTION_LIFECYCLE_V2.FAILED;
697
+ e.lastSeenAt = now;
698
+ if (!e.settledAt) e.settledAt = now;
699
+ flipped.push(_copyExecV2(e));
700
+ }
701
+ }
702
+ return flipped;
703
+ }
704
+
705
+ export function getSkillLoaderStatsV2() {
706
+ const skillsByStatus = {};
707
+ for (const v of Object.values(SKILL_MATURITY_V2)) skillsByStatus[v] = 0;
708
+ for (const s of _stateV2.skills.values()) skillsByStatus[s.status]++;
709
+
710
+ const executionsByStatus = {};
711
+ for (const v of Object.values(EXECUTION_LIFECYCLE_V2))
712
+ executionsByStatus[v] = 0;
713
+ for (const e of _stateV2.executions.values()) executionsByStatus[e.status]++;
714
+
715
+ return {
716
+ totalSkillsV2: _stateV2.skills.size,
717
+ totalExecutionsV2: _stateV2.executions.size,
718
+ maxActiveSkillsPerOwner: _stateV2.maxActiveSkillsPerOwner,
719
+ maxPendingExecutionsPerSkill: _stateV2.maxPendingExecutionsPerSkill,
720
+ skillIdleMs: _stateV2.skillIdleMs,
721
+ execStuckMs: _stateV2.execStuckMs,
722
+ skillsByStatus,
723
+ executionsByStatus,
724
+ };
725
+ }
726
+
727
+ export function _resetStateSkillLoaderV2() {
728
+ _stateV2.skills.clear();
729
+ _stateV2.executions.clear();
730
+ _stateV2.maxActiveSkillsPerOwner = SKILL_DEFAULT_MAX_ACTIVE_PER_OWNER;
731
+ _stateV2.maxPendingExecutionsPerSkill =
732
+ SKILL_DEFAULT_MAX_PENDING_EXECUTIONS_PER_SKILL;
733
+ _stateV2.skillIdleMs = SKILL_DEFAULT_SKILL_IDLE_MS;
734
+ _stateV2.execStuckMs = SKILL_DEFAULT_EXEC_STUCK_MS;
735
+ }
@@ -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
+ }