chainlesschain 0.81.0 → 0.143.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 (209) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +62 -0
  4. package/src/commands/activitypub.js +61 -0
  5. package/src/commands/agent-network.js +254 -1
  6. package/src/commands/agent.js +117 -0
  7. package/src/commands/audit.js +302 -0
  8. package/src/commands/automation.js +271 -1
  9. package/src/commands/bi.js +61 -0
  10. package/src/commands/bm25.js +78 -0
  11. package/src/commands/browse.js +64 -0
  12. package/src/commands/ccron.js +78 -0
  13. package/src/commands/codegen.js +224 -0
  14. package/src/commands/collab.js +341 -0
  15. package/src/commands/compliance.js +1075 -0
  16. package/src/commands/compt.js +78 -0
  17. package/src/commands/consol.js +231 -0
  18. package/src/commands/cowork.js +263 -0
  19. package/src/commands/crosschain.js +62 -0
  20. package/src/commands/dao.js +62 -0
  21. package/src/commands/dbevo.js +284 -0
  22. package/src/commands/dev.js +252 -0
  23. package/src/commands/did.js +358 -0
  24. package/src/commands/dlp.js +61 -0
  25. package/src/commands/economy.js +56 -0
  26. package/src/commands/encrypt.js +341 -0
  27. package/src/commands/evolution.js +56 -0
  28. package/src/commands/evomap.js +61 -0
  29. package/src/commands/export.js +256 -1
  30. package/src/commands/fflag.js +178 -0
  31. package/src/commands/fusion.js +258 -0
  32. package/src/commands/git.js +45 -0
  33. package/src/commands/governance.js +325 -0
  34. package/src/commands/hardening.js +411 -0
  35. package/src/commands/hmemory.js +56 -0
  36. package/src/commands/hook.js +148 -0
  37. package/src/commands/import.js +252 -0
  38. package/src/commands/incentive.js +322 -0
  39. package/src/commands/inference.js +42 -0
  40. package/src/commands/infra.js +244 -0
  41. package/src/commands/instinct.js +260 -0
  42. package/src/commands/ipfs.js +318 -0
  43. package/src/commands/itbudget.js +45 -0
  44. package/src/commands/kg.js +387 -0
  45. package/src/commands/llm.js +263 -0
  46. package/src/commands/lowcode.js +44 -0
  47. package/src/commands/matrix.js +62 -0
  48. package/src/commands/mcp.js +221 -0
  49. package/src/commands/mcpscaf.js +41 -0
  50. package/src/commands/meminj.js +41 -0
  51. package/src/commands/memory.js +248 -0
  52. package/src/commands/multimodal.js +296 -0
  53. package/src/commands/nlprog.js +356 -0
  54. package/src/commands/nostr.js +62 -0
  55. package/src/commands/note.js +244 -0
  56. package/src/commands/ops.js +354 -0
  57. package/src/commands/orchestrate.js +166 -0
  58. package/src/commands/orchgov.js +45 -0
  59. package/src/commands/org.js +277 -0
  60. package/src/commands/p2p.js +390 -0
  61. package/src/commands/pdfp.js +78 -0
  62. package/src/commands/perception.js +290 -0
  63. package/src/commands/perf.js +39 -0
  64. package/src/commands/perm.js +45 -0
  65. package/src/commands/permmem.js +251 -0
  66. package/src/commands/pipeline.js +57 -1
  67. package/src/commands/planmode.js +45 -0
  68. package/src/commands/plugin-ecosystem.js +273 -0
  69. package/src/commands/pqc.js +393 -0
  70. package/src/commands/promcomp.js +82 -0
  71. package/src/commands/quantization.js +351 -0
  72. package/src/commands/rcache.js +271 -0
  73. package/src/commands/recommend.js +382 -0
  74. package/src/commands/runtime.js +307 -0
  75. package/src/commands/scim.js +262 -0
  76. package/src/commands/seshhook.js +41 -0
  77. package/src/commands/seshsearch.js +41 -0
  78. package/src/commands/seshtail.js +41 -0
  79. package/src/commands/seshu.js +41 -0
  80. package/src/commands/session.js +258 -0
  81. package/src/commands/sganal.js +78 -0
  82. package/src/commands/siem.js +40 -0
  83. package/src/commands/skill.js +267 -1
  84. package/src/commands/slotfill.js +41 -0
  85. package/src/commands/social.js +290 -0
  86. package/src/commands/sso.js +186 -1
  87. package/src/commands/svccont.js +45 -0
  88. package/src/commands/sync.js +256 -0
  89. package/src/commands/tech.js +338 -0
  90. package/src/commands/tenant.js +351 -0
  91. package/src/commands/tms.js +45 -0
  92. package/src/commands/tokens.js +269 -0
  93. package/src/commands/topiccls.js +45 -0
  94. package/src/commands/trust.js +249 -0
  95. package/src/commands/uprof.js +45 -0
  96. package/src/commands/vcheck.js +78 -0
  97. package/src/commands/wallet.js +277 -0
  98. package/src/commands/webfetch.js +41 -0
  99. package/src/commands/workflow.js +171 -0
  100. package/src/commands/zkp.js +62 -0
  101. package/src/harness/prompt-compressor.js +331 -0
  102. package/src/index.js +65 -1
  103. package/src/lib/a2a-protocol.js +105 -0
  104. package/src/lib/activitypub-bridge.js +105 -0
  105. package/src/lib/agent-coordinator.js +325 -0
  106. package/src/lib/agent-economy.js +105 -0
  107. package/src/lib/agent-network.js +387 -0
  108. package/src/lib/agent-router.js +395 -0
  109. package/src/lib/aiops.js +478 -0
  110. package/src/lib/app-builder.js +105 -0
  111. package/src/lib/audit-logger.js +379 -0
  112. package/src/lib/automation-engine.js +330 -0
  113. package/src/lib/autonomous-agent.js +105 -0
  114. package/src/lib/autonomous-developer.js +350 -0
  115. package/src/lib/bi-engine.js +105 -0
  116. package/src/lib/bm25-search.js +81 -0
  117. package/src/lib/browser-automation.js +105 -0
  118. package/src/lib/code-agent.js +323 -0
  119. package/src/lib/collaboration-governance.js +364 -0
  120. package/src/lib/community-governance.js +436 -0
  121. package/src/lib/compliance-framework-reporter.js +105 -0
  122. package/src/lib/compliance-manager.js +434 -0
  123. package/src/lib/compression-telemetry.js +81 -0
  124. package/src/lib/content-recommendation.js +469 -0
  125. package/src/lib/content-recommender.js +105 -0
  126. package/src/lib/cowork-cron.js +81 -0
  127. package/src/lib/cowork-task-runner.js +105 -0
  128. package/src/lib/cross-chain.js +105 -0
  129. package/src/lib/crypto-manager.js +350 -0
  130. package/src/lib/dao-governance.js +105 -0
  131. package/src/lib/dbevo.js +338 -0
  132. package/src/lib/decentral-infra.js +340 -0
  133. package/src/lib/did-manager.js +367 -0
  134. package/src/lib/dlp-engine.js +105 -0
  135. package/src/lib/evolution-system.js +105 -0
  136. package/src/lib/evomap-manager.js +105 -0
  137. package/src/lib/execution-backend.js +105 -0
  138. package/src/lib/feature-flags.js +85 -0
  139. package/src/lib/git-integration.js +105 -0
  140. package/src/lib/hardening-manager.js +348 -0
  141. package/src/lib/hierarchical-memory.js +105 -0
  142. package/src/lib/hook-manager.js +380 -0
  143. package/src/lib/inference-network.js +105 -0
  144. package/src/lib/instinct-manager.js +332 -0
  145. package/src/lib/ipfs-storage.js +334 -0
  146. package/src/lib/iteration-budget.js +105 -0
  147. package/src/lib/knowledge-exporter.js +381 -0
  148. package/src/lib/knowledge-graph.js +432 -0
  149. package/src/lib/knowledge-importer.js +379 -0
  150. package/src/lib/llm-providers.js +391 -0
  151. package/src/lib/matrix-bridge.js +105 -0
  152. package/src/lib/mcp-registry.js +333 -0
  153. package/src/lib/mcp-scaffold.js +81 -0
  154. package/src/lib/memory-injection.js +81 -0
  155. package/src/lib/memory-manager.js +330 -0
  156. package/src/lib/multimodal.js +346 -0
  157. package/src/lib/nl-programming.js +343 -0
  158. package/src/lib/nostr-bridge.js +105 -0
  159. package/src/lib/note-versioning.js +327 -0
  160. package/src/lib/orchestrator.js +105 -0
  161. package/src/lib/org-manager.js +323 -0
  162. package/src/lib/p2p-manager.js +387 -0
  163. package/src/lib/pdf-parser.js +81 -0
  164. package/src/lib/perception.js +346 -0
  165. package/src/lib/perf-tuning.js +109 -1
  166. package/src/lib/permanent-memory.js +320 -0
  167. package/src/lib/permission-engine.js +81 -0
  168. package/src/lib/pipeline-orchestrator.js +105 -0
  169. package/src/lib/plan-mode.js +81 -0
  170. package/src/lib/plugin-ecosystem.js +377 -0
  171. package/src/lib/pqc-manager.js +368 -0
  172. package/src/lib/prompt-compressor.js +1 -10
  173. package/src/lib/protocol-fusion.js +417 -0
  174. package/src/lib/quantization.js +325 -0
  175. package/src/lib/response-cache.js +327 -0
  176. package/src/lib/scim-manager.js +329 -0
  177. package/src/lib/service-container.js +81 -0
  178. package/src/lib/session-consolidator.js +105 -0
  179. package/src/lib/session-hooks.js +81 -0
  180. package/src/lib/session-manager.js +329 -0
  181. package/src/lib/session-search.js +81 -0
  182. package/src/lib/session-tail.js +81 -0
  183. package/src/lib/session-usage.js +83 -0
  184. package/src/lib/siem-exporter.js +105 -0
  185. package/src/lib/skill-loader.js +377 -0
  186. package/src/lib/slot-filler.js +81 -0
  187. package/src/lib/social-graph-analytics.js +81 -0
  188. package/src/lib/social-graph.js +81 -0
  189. package/src/lib/social-manager.js +326 -0
  190. package/src/lib/sso-manager.js +332 -0
  191. package/src/lib/sub-agent-registry.js +110 -0
  192. package/src/lib/sync-manager.js +326 -0
  193. package/src/lib/task-model-selector.js +81 -0
  194. package/src/lib/tech-learning-engine.js +369 -0
  195. package/src/lib/tenant-saas.js +460 -0
  196. package/src/lib/threat-intel.js +335 -0
  197. package/src/lib/todo-manager.js +105 -0
  198. package/src/lib/token-incentive.js +293 -0
  199. package/src/lib/token-tracker.js +329 -0
  200. package/src/lib/topic-classifier.js +105 -0
  201. package/src/lib/trust-security.js +390 -0
  202. package/src/lib/ueba.js +389 -0
  203. package/src/lib/universal-runtime.js +325 -0
  204. package/src/lib/user-profile.js +81 -0
  205. package/src/lib/version-checker.js +81 -0
  206. package/src/lib/wallet-manager.js +326 -0
  207. package/src/lib/web-fetch.js +81 -0
  208. package/src/lib/workflow-engine.js +322 -0
  209. package/src/lib/zkp-engine.js +105 -0
@@ -705,3 +705,84 @@ export function analyticsStats(snapshot, opts = {}) {
705
705
  generatedAt: new Date().toISOString(),
706
706
  };
707
707
  }
708
+
709
+
710
+ // =====================================================================
711
+ // Social Graph Analytics V2 governance overlay
712
+ // =====================================================================
713
+ export const SGAN_PROFILE_MATURITY_V2 = Object.freeze({ PENDING: "pending", ACTIVE: "active", STALE: "stale", ARCHIVED: "archived" });
714
+ export const SGAN_RUN_LIFECYCLE_V2 = Object.freeze({ QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled" });
715
+ const _sganPTrans = new Map([
716
+ [SGAN_PROFILE_MATURITY_V2.PENDING, new Set([SGAN_PROFILE_MATURITY_V2.ACTIVE, SGAN_PROFILE_MATURITY_V2.ARCHIVED])],
717
+ [SGAN_PROFILE_MATURITY_V2.ACTIVE, new Set([SGAN_PROFILE_MATURITY_V2.STALE, SGAN_PROFILE_MATURITY_V2.ARCHIVED])],
718
+ [SGAN_PROFILE_MATURITY_V2.STALE, new Set([SGAN_PROFILE_MATURITY_V2.ACTIVE, SGAN_PROFILE_MATURITY_V2.ARCHIVED])],
719
+ [SGAN_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
720
+ ]);
721
+ const _sganPTerminal = new Set([SGAN_PROFILE_MATURITY_V2.ARCHIVED]);
722
+ const _sganJTrans = new Map([
723
+ [SGAN_RUN_LIFECYCLE_V2.QUEUED, new Set([SGAN_RUN_LIFECYCLE_V2.RUNNING, SGAN_RUN_LIFECYCLE_V2.CANCELLED])],
724
+ [SGAN_RUN_LIFECYCLE_V2.RUNNING, new Set([SGAN_RUN_LIFECYCLE_V2.COMPLETED, SGAN_RUN_LIFECYCLE_V2.FAILED, SGAN_RUN_LIFECYCLE_V2.CANCELLED])],
725
+ [SGAN_RUN_LIFECYCLE_V2.COMPLETED, new Set()],
726
+ [SGAN_RUN_LIFECYCLE_V2.FAILED, new Set()],
727
+ [SGAN_RUN_LIFECYCLE_V2.CANCELLED, new Set()],
728
+ ]);
729
+ const _sganPsV2 = new Map();
730
+ const _sganJsV2 = new Map();
731
+ let _sganMaxActive = 6, _sganMaxPending = 12, _sganIdleMs = 30 * 24 * 60 * 60 * 1000, _sganStuckMs = 60 * 1000;
732
+ function _sganPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
733
+ function _sganCheckP(from, to) { const a = _sganPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid sgan profile transition ${from} → ${to}`); }
734
+ function _sganCheckJ(from, to) { const a = _sganJTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid sgan run transition ${from} → ${to}`); }
735
+ function _sganCountActive(owner) { let c = 0; for (const p of _sganPsV2.values()) if (p.owner === owner && p.status === SGAN_PROFILE_MATURITY_V2.ACTIVE) c++; return c; }
736
+ function _sganCountPending(profileId) { let c = 0; for (const j of _sganJsV2.values()) if (j.profileId === profileId && (j.status === SGAN_RUN_LIFECYCLE_V2.QUEUED || j.status === SGAN_RUN_LIFECYCLE_V2.RUNNING)) c++; return c; }
737
+ export function setMaxActiveSganProfilesPerOwnerV2(n) { _sganMaxActive = _sganPos(n, "maxActiveSganProfilesPerOwner"); }
738
+ export function getMaxActiveSganProfilesPerOwnerV2() { return _sganMaxActive; }
739
+ export function setMaxPendingSganRunsPerProfileV2(n) { _sganMaxPending = _sganPos(n, "maxPendingSganRunsPerProfile"); }
740
+ export function getMaxPendingSganRunsPerProfileV2() { return _sganMaxPending; }
741
+ export function setSganProfileIdleMsV2(n) { _sganIdleMs = _sganPos(n, "sganProfileIdleMs"); }
742
+ export function getSganProfileIdleMsV2() { return _sganIdleMs; }
743
+ export function setSganRunStuckMsV2(n) { _sganStuckMs = _sganPos(n, "sganRunStuckMs"); }
744
+ export function getSganRunStuckMsV2() { return _sganStuckMs; }
745
+ export function _resetStateSocialGraphAnalyticsV2() { _sganPsV2.clear(); _sganJsV2.clear(); _sganMaxActive = 6; _sganMaxPending = 12; _sganIdleMs = 30 * 24 * 60 * 60 * 1000; _sganStuckMs = 60 * 1000; }
746
+ export function registerSganProfileV2({ id, owner, algorithm, metadata } = {}) {
747
+ if (!id || !owner) throw new Error("id and owner required");
748
+ if (_sganPsV2.has(id)) throw new Error(`sgan profile ${id} already exists`);
749
+ const now = Date.now();
750
+ const p = { id, owner, algorithm: algorithm || "centrality", status: SGAN_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, lastTouchedAt: now, activatedAt: null, archivedAt: null, metadata: { ...(metadata || {}) } };
751
+ _sganPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
752
+ }
753
+ export function activateSganProfileV2(id) {
754
+ const p = _sganPsV2.get(id); if (!p) throw new Error(`sgan profile ${id} not found`);
755
+ const isInitial = p.status === SGAN_PROFILE_MATURITY_V2.PENDING;
756
+ _sganCheckP(p.status, SGAN_PROFILE_MATURITY_V2.ACTIVE);
757
+ if (isInitial && _sganCountActive(p.owner) >= _sganMaxActive) throw new Error(`max active sgan profiles for owner ${p.owner} reached`);
758
+ const now = Date.now(); p.status = SGAN_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now;
759
+ if (!p.activatedAt) p.activatedAt = now;
760
+ return { ...p, metadata: { ...p.metadata } };
761
+ }
762
+ export function staleSganProfileV2(id) { const p = _sganPsV2.get(id); if (!p) throw new Error(`sgan profile ${id} not found`); _sganCheckP(p.status, SGAN_PROFILE_MATURITY_V2.STALE); p.status = SGAN_PROFILE_MATURITY_V2.STALE; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
763
+ export function archiveSganProfileV2(id) { const p = _sganPsV2.get(id); if (!p) throw new Error(`sgan profile ${id} not found`); _sganCheckP(p.status, SGAN_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = SGAN_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
764
+ export function touchSganProfileV2(id) { const p = _sganPsV2.get(id); if (!p) throw new Error(`sgan profile ${id} not found`); if (_sganPTerminal.has(p.status)) throw new Error(`cannot touch terminal sgan profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
765
+ export function getSganProfileV2(id) { const p = _sganPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
766
+ export function listSganProfilesV2() { return [..._sganPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
767
+ export function createSganRunV2({ id, profileId, snapshotId, metadata } = {}) {
768
+ if (!id || !profileId) throw new Error("id and profileId required");
769
+ if (_sganJsV2.has(id)) throw new Error(`sgan run ${id} already exists`);
770
+ if (!_sganPsV2.has(profileId)) throw new Error(`sgan profile ${profileId} not found`);
771
+ if (_sganCountPending(profileId) >= _sganMaxPending) throw new Error(`max pending sgan runs for profile ${profileId} reached`);
772
+ const now = Date.now();
773
+ const j = { id, profileId, snapshotId: snapshotId || "", status: SGAN_RUN_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
774
+ _sganJsV2.set(id, j); return { ...j, metadata: { ...j.metadata } };
775
+ }
776
+ export function runningSganRunV2(id) { const j = _sganJsV2.get(id); if (!j) throw new Error(`sgan run ${id} not found`); _sganCheckJ(j.status, SGAN_RUN_LIFECYCLE_V2.RUNNING); const now = Date.now(); j.status = SGAN_RUN_LIFECYCLE_V2.RUNNING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
777
+ export function completeSganRunV2(id) { const j = _sganJsV2.get(id); if (!j) throw new Error(`sgan run ${id} not found`); _sganCheckJ(j.status, SGAN_RUN_LIFECYCLE_V2.COMPLETED); const now = Date.now(); j.status = SGAN_RUN_LIFECYCLE_V2.COMPLETED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
778
+ export function failSganRunV2(id, reason) { const j = _sganJsV2.get(id); if (!j) throw new Error(`sgan run ${id} not found`); _sganCheckJ(j.status, SGAN_RUN_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = SGAN_RUN_LIFECYCLE_V2.FAILED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.failReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
779
+ export function cancelSganRunV2(id, reason) { const j = _sganJsV2.get(id); if (!j) throw new Error(`sgan run ${id} not found`); _sganCheckJ(j.status, SGAN_RUN_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = SGAN_RUN_LIFECYCLE_V2.CANCELLED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.cancelReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
780
+ export function getSganRunV2(id) { const j = _sganJsV2.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
781
+ export function listSganRunsV2() { return [..._sganJsV2.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
782
+ export function autoStaleIdleSganProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _sganPsV2.values()) if (p.status === SGAN_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _sganIdleMs) { p.status = SGAN_PROFILE_MATURITY_V2.STALE; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
783
+ export function autoFailStuckSganRunsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _sganJsV2.values()) if (j.status === SGAN_RUN_LIFECYCLE_V2.RUNNING && j.startedAt != null && (t - j.startedAt) >= _sganStuckMs) { j.status = SGAN_RUN_LIFECYCLE_V2.FAILED; j.updatedAt = t; if (!j.settledAt) j.settledAt = t; j.metadata.failReason = "auto-fail-stuck"; flipped.push(j.id); } return { flipped, count: flipped.length }; }
784
+ export function getSocialGraphAnalyticsGovStatsV2() {
785
+ const profilesByStatus = {}; for (const v of Object.values(SGAN_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _sganPsV2.values()) profilesByStatus[p.status]++;
786
+ const runsByStatus = {}; for (const v of Object.values(SGAN_RUN_LIFECYCLE_V2)) runsByStatus[v] = 0; for (const j of _sganJsV2.values()) runsByStatus[j.status]++;
787
+ return { totalSganProfilesV2: _sganPsV2.size, totalSganRunsV2: _sganJsV2.size, maxActiveSganProfilesPerOwner: _sganMaxActive, maxPendingSganRunsPerProfile: _sganMaxPending, sganProfileIdleMs: _sganIdleMs, sganRunStuckMs: _sganStuckMs, profilesByStatus, runsByStatus };
788
+ }
@@ -406,3 +406,84 @@ export function _resetState() {
406
406
  _nodes.clear();
407
407
  _bus.removeAllListeners();
408
408
  }
409
+
410
+ // ===== V2 Surface: Social Graph governance overlay (CLI v0.141.0) =====
411
+ export const SG_NODE_MATURITY_V2 = Object.freeze({
412
+ PENDING: "pending", ACTIVE: "active", INACTIVE: "inactive", REMOVED: "removed",
413
+ });
414
+ export const SG_EDGE_LIFECYCLE_V2 = Object.freeze({
415
+ PROPOSED: "proposed", ESTABLISHED: "established", SEVERED: "severed", EXPIRED: "expired", CANCELLED: "cancelled",
416
+ });
417
+ const _sgNTrans = new Map([
418
+ [SG_NODE_MATURITY_V2.PENDING, new Set([SG_NODE_MATURITY_V2.ACTIVE, SG_NODE_MATURITY_V2.REMOVED])],
419
+ [SG_NODE_MATURITY_V2.ACTIVE, new Set([SG_NODE_MATURITY_V2.INACTIVE, SG_NODE_MATURITY_V2.REMOVED])],
420
+ [SG_NODE_MATURITY_V2.INACTIVE, new Set([SG_NODE_MATURITY_V2.ACTIVE, SG_NODE_MATURITY_V2.REMOVED])],
421
+ [SG_NODE_MATURITY_V2.REMOVED, new Set()],
422
+ ]);
423
+ const _sgNTerminal = new Set([SG_NODE_MATURITY_V2.REMOVED]);
424
+ const _sgETrans = new Map([
425
+ [SG_EDGE_LIFECYCLE_V2.PROPOSED, new Set([SG_EDGE_LIFECYCLE_V2.ESTABLISHED, SG_EDGE_LIFECYCLE_V2.CANCELLED])],
426
+ [SG_EDGE_LIFECYCLE_V2.ESTABLISHED, new Set([SG_EDGE_LIFECYCLE_V2.SEVERED, SG_EDGE_LIFECYCLE_V2.EXPIRED])],
427
+ [SG_EDGE_LIFECYCLE_V2.SEVERED, new Set()],
428
+ [SG_EDGE_LIFECYCLE_V2.EXPIRED, new Set()],
429
+ [SG_EDGE_LIFECYCLE_V2.CANCELLED, new Set()],
430
+ ]);
431
+ const _sgNsV2 = new Map();
432
+ const _sgEsV2 = new Map();
433
+ let _sgMaxActivePerOwner = 50, _sgMaxPendingEdgesPerNode = 100, _sgIdleMs = 60 * 24 * 60 * 60 * 1000, _sgStuckMs = 14 * 24 * 60 * 60 * 1000;
434
+ function _sgPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
435
+ function _sgCheckN(from, to) { const a = _sgNTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid sg node transition ${from} → ${to}`); }
436
+ function _sgCheckE(from, to) { const a = _sgETrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid sg edge transition ${from} → ${to}`); }
437
+ export function setMaxActiveSgNodesPerOwnerV2(n) { _sgMaxActivePerOwner = _sgPos(n, "maxActiveSgNodesPerOwner"); }
438
+ export function getMaxActiveSgNodesPerOwnerV2() { return _sgMaxActivePerOwner; }
439
+ export function setMaxPendingSgEdgesPerNodeV2(n) { _sgMaxPendingEdgesPerNode = _sgPos(n, "maxPendingSgEdgesPerNode"); }
440
+ export function getMaxPendingSgEdgesPerNodeV2() { return _sgMaxPendingEdgesPerNode; }
441
+ export function setSgNodeIdleMsV2(n) { _sgIdleMs = _sgPos(n, "sgNodeIdleMs"); }
442
+ export function getSgNodeIdleMsV2() { return _sgIdleMs; }
443
+ export function setSgEdgeStuckMsV2(n) { _sgStuckMs = _sgPos(n, "sgEdgeStuckMs"); }
444
+ export function getSgEdgeStuckMsV2() { return _sgStuckMs; }
445
+ export function _resetStateSocialGraphV2() { _sgNsV2.clear(); _sgEsV2.clear(); _sgMaxActivePerOwner = 50; _sgMaxPendingEdgesPerNode = 100; _sgIdleMs = 60 * 24 * 60 * 60 * 1000; _sgStuckMs = 14 * 24 * 60 * 60 * 1000; }
446
+ export function registerSgNodeV2({ id, owner, handle, metadata } = {}) {
447
+ if (!id) throw new Error("sg node id required"); if (!owner) throw new Error("sg node owner required");
448
+ if (_sgNsV2.has(id)) throw new Error(`sg node ${id} already registered`);
449
+ const now = Date.now();
450
+ const n = { id, owner, handle: handle || id, status: SG_NODE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, removedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
451
+ _sgNsV2.set(id, n); return { ...n, metadata: { ...n.metadata } };
452
+ }
453
+ function _sgCountActive(owner) { let n = 0; for (const v of _sgNsV2.values()) if (v.owner === owner && v.status === SG_NODE_MATURITY_V2.ACTIVE) n++; return n; }
454
+ export function activateSgNodeV2(id) {
455
+ const n = _sgNsV2.get(id); if (!n) throw new Error(`sg node ${id} not found`);
456
+ _sgCheckN(n.status, SG_NODE_MATURITY_V2.ACTIVE);
457
+ const recovery = n.status === SG_NODE_MATURITY_V2.INACTIVE;
458
+ if (!recovery && _sgCountActive(n.owner) >= _sgMaxActivePerOwner) throw new Error(`max active sg nodes for owner ${n.owner} reached`);
459
+ const now = Date.now(); n.status = SG_NODE_MATURITY_V2.ACTIVE; n.updatedAt = now; n.lastTouchedAt = now; if (!n.activatedAt) n.activatedAt = now;
460
+ return { ...n, metadata: { ...n.metadata } };
461
+ }
462
+ export function deactivateSgNodeV2(id) { const n = _sgNsV2.get(id); if (!n) throw new Error(`sg node ${id} not found`); _sgCheckN(n.status, SG_NODE_MATURITY_V2.INACTIVE); n.status = SG_NODE_MATURITY_V2.INACTIVE; n.updatedAt = Date.now(); return { ...n, metadata: { ...n.metadata } }; }
463
+ export function removeSgNodeV2(id) { const n = _sgNsV2.get(id); if (!n) throw new Error(`sg node ${id} not found`); _sgCheckN(n.status, SG_NODE_MATURITY_V2.REMOVED); const now = Date.now(); n.status = SG_NODE_MATURITY_V2.REMOVED; n.updatedAt = now; if (!n.removedAt) n.removedAt = now; return { ...n, metadata: { ...n.metadata } }; }
464
+ export function touchSgNodeV2(id) { const n = _sgNsV2.get(id); if (!n) throw new Error(`sg node ${id} not found`); if (_sgNTerminal.has(n.status)) throw new Error(`cannot touch terminal sg node ${id}`); const now = Date.now(); n.lastTouchedAt = now; n.updatedAt = now; return { ...n, metadata: { ...n.metadata } }; }
465
+ export function getSgNodeV2(id) { const n = _sgNsV2.get(id); if (!n) return null; return { ...n, metadata: { ...n.metadata } }; }
466
+ export function listSgNodesV2() { return [..._sgNsV2.values()].map((n) => ({ ...n, metadata: { ...n.metadata } })); }
467
+ function _sgCountPending(nodeId) { let n = 0; for (const e of _sgEsV2.values()) if (e.nodeId === nodeId && e.status === SG_EDGE_LIFECYCLE_V2.PROPOSED) n++; return n; }
468
+ export function createSgEdgeV2({ id, nodeId, targetId, metadata } = {}) {
469
+ if (!id) throw new Error("sg edge id required"); if (!nodeId) throw new Error("sg edge nodeId required");
470
+ if (_sgEsV2.has(id)) throw new Error(`sg edge ${id} already exists`);
471
+ if (!_sgNsV2.has(nodeId)) throw new Error(`sg node ${nodeId} not found`);
472
+ if (_sgCountPending(nodeId) >= _sgMaxPendingEdgesPerNode) throw new Error(`max pending sg edges for node ${nodeId} reached`);
473
+ const now = Date.now();
474
+ const e = { id, nodeId, targetId: targetId || "", status: SG_EDGE_LIFECYCLE_V2.PROPOSED, createdAt: now, updatedAt: now, startedAt: now, settledAt: null, metadata: { ...(metadata || {}) } };
475
+ _sgEsV2.set(id, e); return { ...e, metadata: { ...e.metadata } };
476
+ }
477
+ export function establishSgEdgeV2(id) { const e = _sgEsV2.get(id); if (!e) throw new Error(`sg edge ${id} not found`); _sgCheckE(e.status, SG_EDGE_LIFECYCLE_V2.ESTABLISHED); const now = Date.now(); e.status = SG_EDGE_LIFECYCLE_V2.ESTABLISHED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; return { ...e, metadata: { ...e.metadata } }; }
478
+ export function severSgEdgeV2(id, reason) { const e = _sgEsV2.get(id); if (!e) throw new Error(`sg edge ${id} not found`); _sgCheckE(e.status, SG_EDGE_LIFECYCLE_V2.SEVERED); const now = Date.now(); e.status = SG_EDGE_LIFECYCLE_V2.SEVERED; e.updatedAt = now; if (reason) e.metadata.severReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
479
+ export function expireSgEdgeV2(id) { const e = _sgEsV2.get(id); if (!e) throw new Error(`sg edge ${id} not found`); _sgCheckE(e.status, SG_EDGE_LIFECYCLE_V2.EXPIRED); const now = Date.now(); e.status = SG_EDGE_LIFECYCLE_V2.EXPIRED; e.updatedAt = now; return { ...e, metadata: { ...e.metadata } }; }
480
+ export function cancelSgEdgeV2(id, reason) { const e = _sgEsV2.get(id); if (!e) throw new Error(`sg edge ${id} not found`); _sgCheckE(e.status, SG_EDGE_LIFECYCLE_V2.CANCELLED); const now = Date.now(); e.status = SG_EDGE_LIFECYCLE_V2.CANCELLED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.cancelReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
481
+ export function getSgEdgeV2(id) { const e = _sgEsV2.get(id); if (!e) return null; return { ...e, metadata: { ...e.metadata } }; }
482
+ export function listSgEdgesV2() { return [..._sgEsV2.values()].map((e) => ({ ...e, metadata: { ...e.metadata } })); }
483
+ export function autoDeactivateIdleSgNodesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const n of _sgNsV2.values()) if (n.status === SG_NODE_MATURITY_V2.ACTIVE && (t - n.lastTouchedAt) >= _sgIdleMs) { n.status = SG_NODE_MATURITY_V2.INACTIVE; n.updatedAt = t; flipped.push(n.id); } return { flipped, count: flipped.length }; }
484
+ export function autoExpireStaleSgEdgesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const e of _sgEsV2.values()) if (e.status === SG_EDGE_LIFECYCLE_V2.PROPOSED && (t - e.startedAt) >= _sgStuckMs) { e.status = SG_EDGE_LIFECYCLE_V2.CANCELLED; e.updatedAt = t; if (!e.settledAt) e.settledAt = t; e.metadata.cancelReason = "auto-cancel-stale"; flipped.push(e.id); } return { flipped, count: flipped.length }; }
485
+ export function getSocialGraphGovStatsV2() {
486
+ const nodesByStatus = {}; for (const v of Object.values(SG_NODE_MATURITY_V2)) nodesByStatus[v] = 0; for (const n of _sgNsV2.values()) nodesByStatus[n.status]++;
487
+ const edgesByStatus = {}; for (const v of Object.values(SG_EDGE_LIFECYCLE_V2)) edgesByStatus[v] = 0; for (const e of _sgEsV2.values()) edgesByStatus[e.status]++;
488
+ return { totalSgNodesV2: _sgNsV2.size, totalSgEdgesV2: _sgEsV2.size, maxActiveSgNodesPerOwner: _sgMaxActivePerOwner, maxPendingSgEdgesPerNode: _sgMaxPendingEdgesPerNode, sgNodeIdleMs: _sgIdleMs, sgEdgeStuckMs: _sgStuckMs, nodesByStatus, edgesByStatus };
489
+ }
@@ -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
+ }