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
@@ -387,3 +387,383 @@ export function updateHookStats(
387
387
  "UPDATE hooks SET execution_count = ?, error_count = ?, total_execution_time = ?, updated_at = datetime('now') WHERE id = ?",
388
388
  ).run(newCount, newErrorCount, newTotalTime, hookId);
389
389
  }
390
+
391
+ // ===== V2 Surface: Hook Manager governance overlay (CLI v0.132.0) =====
392
+ // In-memory governance for hook profiles + execution lifecycle, independent of
393
+ // the legacy registerHook/executeHooks SQLite-backed path above.
394
+
395
+ export const HOOK_PROFILE_MATURITY_V2 = Object.freeze({
396
+ PENDING: "pending",
397
+ ACTIVE: "active",
398
+ DISABLED: "disabled",
399
+ RETIRED: "retired",
400
+ });
401
+
402
+ export const HOOK_EXEC_LIFECYCLE_V2 = Object.freeze({
403
+ QUEUED: "queued",
404
+ RUNNING: "running",
405
+ COMPLETED: "completed",
406
+ FAILED: "failed",
407
+ CANCELLED: "cancelled",
408
+ });
409
+
410
+ const _hookProfileTransitionsV2 = new Map([
411
+ [
412
+ HOOK_PROFILE_MATURITY_V2.PENDING,
413
+ new Set([
414
+ HOOK_PROFILE_MATURITY_V2.ACTIVE,
415
+ HOOK_PROFILE_MATURITY_V2.RETIRED,
416
+ ]),
417
+ ],
418
+ [
419
+ HOOK_PROFILE_MATURITY_V2.ACTIVE,
420
+ new Set([
421
+ HOOK_PROFILE_MATURITY_V2.DISABLED,
422
+ HOOK_PROFILE_MATURITY_V2.RETIRED,
423
+ ]),
424
+ ],
425
+ [
426
+ HOOK_PROFILE_MATURITY_V2.DISABLED,
427
+ new Set([
428
+ HOOK_PROFILE_MATURITY_V2.ACTIVE,
429
+ HOOK_PROFILE_MATURITY_V2.RETIRED,
430
+ ]),
431
+ ],
432
+ [HOOK_PROFILE_MATURITY_V2.RETIRED, new Set()],
433
+ ]);
434
+ const _hookProfileTerminalV2 = new Set([HOOK_PROFILE_MATURITY_V2.RETIRED]);
435
+
436
+ const _hookExecTransitionsV2 = new Map([
437
+ [
438
+ HOOK_EXEC_LIFECYCLE_V2.QUEUED,
439
+ new Set([HOOK_EXEC_LIFECYCLE_V2.RUNNING, HOOK_EXEC_LIFECYCLE_V2.CANCELLED]),
440
+ ],
441
+ [
442
+ HOOK_EXEC_LIFECYCLE_V2.RUNNING,
443
+ new Set([
444
+ HOOK_EXEC_LIFECYCLE_V2.COMPLETED,
445
+ HOOK_EXEC_LIFECYCLE_V2.FAILED,
446
+ HOOK_EXEC_LIFECYCLE_V2.CANCELLED,
447
+ ]),
448
+ ],
449
+ [HOOK_EXEC_LIFECYCLE_V2.COMPLETED, new Set()],
450
+ [HOOK_EXEC_LIFECYCLE_V2.FAILED, new Set()],
451
+ [HOOK_EXEC_LIFECYCLE_V2.CANCELLED, new Set()],
452
+ ]);
453
+ const _hookExecTerminalV2 = new Set([
454
+ HOOK_EXEC_LIFECYCLE_V2.COMPLETED,
455
+ HOOK_EXEC_LIFECYCLE_V2.FAILED,
456
+ HOOK_EXEC_LIFECYCLE_V2.CANCELLED,
457
+ ]);
458
+
459
+ const _hookProfilesV2 = new Map();
460
+ const _hookExecsV2 = new Map();
461
+ let _maxActiveHooksPerOwnerV2 = 20;
462
+ let _maxPendingExecsPerHookV2 = 32;
463
+ let _hookIdleMsV2 = 24 * 60 * 60 * 1000;
464
+ let _hookExecStuckMsV2 = 60 * 1000;
465
+
466
+ function _hookPosIntV2(n, label) {
467
+ const v = Math.floor(Number(n));
468
+ if (!Number.isFinite(v) || v <= 0)
469
+ throw new Error(`${label} must be positive integer`);
470
+ return v;
471
+ }
472
+
473
+ export function setMaxActiveHooksPerOwnerV2(n) {
474
+ _maxActiveHooksPerOwnerV2 = _hookPosIntV2(n, "maxActiveHooksPerOwner");
475
+ }
476
+ export function getMaxActiveHooksPerOwnerV2() {
477
+ return _maxActiveHooksPerOwnerV2;
478
+ }
479
+ export function setMaxPendingExecsPerHookV2(n) {
480
+ _maxPendingExecsPerHookV2 = _hookPosIntV2(n, "maxPendingExecsPerHook");
481
+ }
482
+ export function getMaxPendingExecsPerHookV2() {
483
+ return _maxPendingExecsPerHookV2;
484
+ }
485
+ export function setHookIdleMsV2(n) {
486
+ _hookIdleMsV2 = _hookPosIntV2(n, "hookIdleMs");
487
+ }
488
+ export function getHookIdleMsV2() {
489
+ return _hookIdleMsV2;
490
+ }
491
+ export function setHookExecStuckMsV2(n) {
492
+ _hookExecStuckMsV2 = _hookPosIntV2(n, "hookExecStuckMs");
493
+ }
494
+ export function getHookExecStuckMsV2() {
495
+ return _hookExecStuckMsV2;
496
+ }
497
+
498
+ export function _resetStateHookManagerV2() {
499
+ _hookProfilesV2.clear();
500
+ _hookExecsV2.clear();
501
+ _maxActiveHooksPerOwnerV2 = 20;
502
+ _maxPendingExecsPerHookV2 = 32;
503
+ _hookIdleMsV2 = 24 * 60 * 60 * 1000;
504
+ _hookExecStuckMsV2 = 60 * 1000;
505
+ }
506
+
507
+ export function registerHookProfileV2({ id, owner, event, metadata } = {}) {
508
+ if (!id || typeof id !== "string") throw new Error("id is required");
509
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
510
+ if (_hookProfilesV2.has(id))
511
+ throw new Error(`hook profile ${id} already registered`);
512
+ const now = Date.now();
513
+ const p = {
514
+ id,
515
+ owner,
516
+ event: event || "*",
517
+ status: HOOK_PROFILE_MATURITY_V2.PENDING,
518
+ createdAt: now,
519
+ updatedAt: now,
520
+ activatedAt: null,
521
+ retiredAt: null,
522
+ lastTouchedAt: now,
523
+ metadata: { ...(metadata || {}) },
524
+ };
525
+ _hookProfilesV2.set(id, p);
526
+ return { ...p, metadata: { ...p.metadata } };
527
+ }
528
+
529
+ function _hookProfileCheck(from, to) {
530
+ const allowed = _hookProfileTransitionsV2.get(from);
531
+ if (!allowed || !allowed.has(to))
532
+ throw new Error(`invalid hook profile transition ${from} → ${to}`);
533
+ }
534
+
535
+ function _countActiveHooksByOwner(owner) {
536
+ let n = 0;
537
+ for (const p of _hookProfilesV2.values()) {
538
+ if (p.owner === owner && p.status === HOOK_PROFILE_MATURITY_V2.ACTIVE) n++;
539
+ }
540
+ return n;
541
+ }
542
+
543
+ export function activateHookProfileV2(id) {
544
+ const p = _hookProfilesV2.get(id);
545
+ if (!p) throw new Error(`hook profile ${id} not found`);
546
+ _hookProfileCheck(p.status, HOOK_PROFILE_MATURITY_V2.ACTIVE);
547
+ const isRecovery = p.status === HOOK_PROFILE_MATURITY_V2.DISABLED;
548
+ if (!isRecovery) {
549
+ const active = _countActiveHooksByOwner(p.owner);
550
+ if (active >= _maxActiveHooksPerOwnerV2) {
551
+ throw new Error(
552
+ `max active hooks per owner (${_maxActiveHooksPerOwnerV2}) reached for ${p.owner}`,
553
+ );
554
+ }
555
+ }
556
+ const now = Date.now();
557
+ p.status = HOOK_PROFILE_MATURITY_V2.ACTIVE;
558
+ p.updatedAt = now;
559
+ p.lastTouchedAt = now;
560
+ if (!p.activatedAt) p.activatedAt = now;
561
+ return { ...p, metadata: { ...p.metadata } };
562
+ }
563
+
564
+ export function disableHookProfileV2(id) {
565
+ const p = _hookProfilesV2.get(id);
566
+ if (!p) throw new Error(`hook profile ${id} not found`);
567
+ _hookProfileCheck(p.status, HOOK_PROFILE_MATURITY_V2.DISABLED);
568
+ const now = Date.now();
569
+ p.status = HOOK_PROFILE_MATURITY_V2.DISABLED;
570
+ p.updatedAt = now;
571
+ return { ...p, metadata: { ...p.metadata } };
572
+ }
573
+
574
+ export function retireHookProfileV2(id) {
575
+ const p = _hookProfilesV2.get(id);
576
+ if (!p) throw new Error(`hook profile ${id} not found`);
577
+ _hookProfileCheck(p.status, HOOK_PROFILE_MATURITY_V2.RETIRED);
578
+ const now = Date.now();
579
+ p.status = HOOK_PROFILE_MATURITY_V2.RETIRED;
580
+ p.updatedAt = now;
581
+ if (!p.retiredAt) p.retiredAt = now;
582
+ return { ...p, metadata: { ...p.metadata } };
583
+ }
584
+
585
+ export function touchHookProfileV2(id) {
586
+ const p = _hookProfilesV2.get(id);
587
+ if (!p) throw new Error(`hook profile ${id} not found`);
588
+ if (_hookProfileTerminalV2.has(p.status))
589
+ throw new Error(`cannot touch terminal hook profile ${id}`);
590
+ const now = Date.now();
591
+ p.lastTouchedAt = now;
592
+ p.updatedAt = now;
593
+ return { ...p, metadata: { ...p.metadata } };
594
+ }
595
+
596
+ export function getHookProfileV2(id) {
597
+ const p = _hookProfilesV2.get(id);
598
+ if (!p) return null;
599
+ return { ...p, metadata: { ...p.metadata } };
600
+ }
601
+
602
+ export function listHookProfilesV2() {
603
+ return [..._hookProfilesV2.values()].map((p) => ({
604
+ ...p,
605
+ metadata: { ...p.metadata },
606
+ }));
607
+ }
608
+
609
+ function _countPendingExecsByHook(hookId) {
610
+ let n = 0;
611
+ for (const e of _hookExecsV2.values()) {
612
+ if (
613
+ e.hookId === hookId &&
614
+ (e.status === HOOK_EXEC_LIFECYCLE_V2.QUEUED ||
615
+ e.status === HOOK_EXEC_LIFECYCLE_V2.RUNNING)
616
+ )
617
+ n++;
618
+ }
619
+ return n;
620
+ }
621
+
622
+ export function createHookExecV2({ id, hookId, payload, metadata } = {}) {
623
+ if (!id || typeof id !== "string") throw new Error("id is required");
624
+ if (!hookId || typeof hookId !== "string")
625
+ throw new Error("hookId is required");
626
+ if (_hookExecsV2.has(id)) throw new Error(`hook exec ${id} already exists`);
627
+ const hook = _hookProfilesV2.get(hookId);
628
+ if (!hook) throw new Error(`hook profile ${hookId} not found`);
629
+ const pending = _countPendingExecsByHook(hookId);
630
+ if (pending >= _maxPendingExecsPerHookV2) {
631
+ throw new Error(
632
+ `max pending execs per hook (${_maxPendingExecsPerHookV2}) reached for ${hookId}`,
633
+ );
634
+ }
635
+ const now = Date.now();
636
+ const e = {
637
+ id,
638
+ hookId,
639
+ payload: payload || null,
640
+ status: HOOK_EXEC_LIFECYCLE_V2.QUEUED,
641
+ createdAt: now,
642
+ updatedAt: now,
643
+ startedAt: null,
644
+ settledAt: null,
645
+ metadata: { ...(metadata || {}) },
646
+ };
647
+ _hookExecsV2.set(id, e);
648
+ return { ...e, metadata: { ...e.metadata } };
649
+ }
650
+
651
+ function _hookExecCheck(from, to) {
652
+ const allowed = _hookExecTransitionsV2.get(from);
653
+ if (!allowed || !allowed.has(to))
654
+ throw new Error(`invalid hook exec transition ${from} → ${to}`);
655
+ }
656
+
657
+ export function startHookExecV2(id) {
658
+ const e = _hookExecsV2.get(id);
659
+ if (!e) throw new Error(`hook exec ${id} not found`);
660
+ _hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.RUNNING);
661
+ const now = Date.now();
662
+ e.status = HOOK_EXEC_LIFECYCLE_V2.RUNNING;
663
+ e.updatedAt = now;
664
+ if (!e.startedAt) e.startedAt = now;
665
+ return { ...e, metadata: { ...e.metadata } };
666
+ }
667
+
668
+ export function completeHookExecV2(id) {
669
+ const e = _hookExecsV2.get(id);
670
+ if (!e) throw new Error(`hook exec ${id} not found`);
671
+ _hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.COMPLETED);
672
+ const now = Date.now();
673
+ e.status = HOOK_EXEC_LIFECYCLE_V2.COMPLETED;
674
+ e.updatedAt = now;
675
+ if (!e.settledAt) e.settledAt = now;
676
+ return { ...e, metadata: { ...e.metadata } };
677
+ }
678
+
679
+ export function failHookExecV2(id, reason) {
680
+ const e = _hookExecsV2.get(id);
681
+ if (!e) throw new Error(`hook exec ${id} not found`);
682
+ _hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.FAILED);
683
+ const now = Date.now();
684
+ e.status = HOOK_EXEC_LIFECYCLE_V2.FAILED;
685
+ e.updatedAt = now;
686
+ if (!e.settledAt) e.settledAt = now;
687
+ if (reason) e.metadata.failReason = String(reason);
688
+ return { ...e, metadata: { ...e.metadata } };
689
+ }
690
+
691
+ export function cancelHookExecV2(id, reason) {
692
+ const e = _hookExecsV2.get(id);
693
+ if (!e) throw new Error(`hook exec ${id} not found`);
694
+ _hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.CANCELLED);
695
+ const now = Date.now();
696
+ e.status = HOOK_EXEC_LIFECYCLE_V2.CANCELLED;
697
+ e.updatedAt = now;
698
+ if (!e.settledAt) e.settledAt = now;
699
+ if (reason) e.metadata.cancelReason = String(reason);
700
+ return { ...e, metadata: { ...e.metadata } };
701
+ }
702
+
703
+ export function getHookExecV2(id) {
704
+ const e = _hookExecsV2.get(id);
705
+ if (!e) return null;
706
+ return { ...e, metadata: { ...e.metadata } };
707
+ }
708
+
709
+ export function listHookExecsV2() {
710
+ return [..._hookExecsV2.values()].map((e) => ({
711
+ ...e,
712
+ metadata: { ...e.metadata },
713
+ }));
714
+ }
715
+
716
+ export function autoDisableIdleHooksV2({ now } = {}) {
717
+ const t = now ?? Date.now();
718
+ const flipped = [];
719
+ for (const p of _hookProfilesV2.values()) {
720
+ if (
721
+ p.status === HOOK_PROFILE_MATURITY_V2.ACTIVE &&
722
+ t - p.lastTouchedAt >= _hookIdleMsV2
723
+ ) {
724
+ p.status = HOOK_PROFILE_MATURITY_V2.DISABLED;
725
+ p.updatedAt = t;
726
+ flipped.push(p.id);
727
+ }
728
+ }
729
+ return { flipped, count: flipped.length };
730
+ }
731
+
732
+ export function autoFailStuckHookExecsV2({ now } = {}) {
733
+ const t = now ?? Date.now();
734
+ const flipped = [];
735
+ for (const e of _hookExecsV2.values()) {
736
+ if (
737
+ e.status === HOOK_EXEC_LIFECYCLE_V2.RUNNING &&
738
+ e.startedAt != null &&
739
+ t - e.startedAt >= _hookExecStuckMsV2
740
+ ) {
741
+ e.status = HOOK_EXEC_LIFECYCLE_V2.FAILED;
742
+ e.updatedAt = t;
743
+ if (!e.settledAt) e.settledAt = t;
744
+ e.metadata.failReason = "auto-fail-stuck";
745
+ flipped.push(e.id);
746
+ }
747
+ }
748
+ return { flipped, count: flipped.length };
749
+ }
750
+
751
+ export function getHookManagerStatsV2() {
752
+ const profilesByStatus = {};
753
+ for (const s of Object.values(HOOK_PROFILE_MATURITY_V2))
754
+ profilesByStatus[s] = 0;
755
+ for (const p of _hookProfilesV2.values()) profilesByStatus[p.status]++;
756
+ const execsByStatus = {};
757
+ for (const s of Object.values(HOOK_EXEC_LIFECYCLE_V2)) execsByStatus[s] = 0;
758
+ for (const e of _hookExecsV2.values()) execsByStatus[e.status]++;
759
+ return {
760
+ totalProfilesV2: _hookProfilesV2.size,
761
+ totalExecsV2: _hookExecsV2.size,
762
+ maxActiveHooksPerOwner: _maxActiveHooksPerOwnerV2,
763
+ maxPendingExecsPerHook: _maxPendingExecsPerHookV2,
764
+ hookIdleMs: _hookIdleMsV2,
765
+ hookExecStuckMs: _hookExecStuckMsV2,
766
+ profilesByStatus,
767
+ execsByStatus,
768
+ };
769
+ }
@@ -188,3 +188,335 @@ export function generateInstinctPrompt(db) {
188
188
 
189
189
  return lines.join("\n");
190
190
  }
191
+
192
+ /* ═══════════════════════════════════════════════════════════════
193
+ * V2 Surface — Instinct governance layer.
194
+ * Tracks per-user instinct profile maturity + observation lifecycle
195
+ * independent of legacy SQLite instincts table.
196
+ * ═══════════════════════════════════════════════════════════════ */
197
+
198
+ export const PROFILE_MATURITY_V2 = Object.freeze({
199
+ PENDING: "pending",
200
+ ACTIVE: "active",
201
+ DORMANT: "dormant",
202
+ ARCHIVED: "archived",
203
+ });
204
+
205
+ export const OBSERVATION_LIFECYCLE_V2 = Object.freeze({
206
+ CAPTURED: "captured",
207
+ REVIEWED: "reviewed",
208
+ REINFORCED: "reinforced",
209
+ DISCARDED: "discarded",
210
+ PROMOTED: "promoted",
211
+ });
212
+
213
+ const PROFILE_TRANSITIONS_V2 = new Map([
214
+ ["pending", new Set(["active", "archived"])],
215
+ ["active", new Set(["dormant", "archived"])],
216
+ ["dormant", new Set(["active", "archived"])],
217
+ ["archived", new Set()],
218
+ ]);
219
+ const PROFILE_TERMINALS_V2 = new Set(["archived"]);
220
+
221
+ const OBS_TRANSITIONS_V2 = new Map([
222
+ ["captured", new Set(["reviewed", "discarded"])],
223
+ ["reviewed", new Set(["reinforced", "discarded", "promoted"])],
224
+ ["reinforced", new Set(["promoted", "discarded"])],
225
+ ["discarded", new Set()],
226
+ ["promoted", new Set()],
227
+ ]);
228
+ const OBS_TERMINALS_V2 = new Set(["discarded", "promoted"]);
229
+
230
+ export const INSTINCT_DEFAULT_MAX_ACTIVE_PROFILES_PER_USER = 5;
231
+ export const INSTINCT_DEFAULT_MAX_PENDING_OBS_PER_PROFILE = 100;
232
+ export const INSTINCT_DEFAULT_PROFILE_IDLE_MS = 1000 * 60 * 60 * 24 * 60; // 60 days
233
+ export const INSTINCT_DEFAULT_OBS_STUCK_MS = 1000 * 60 * 60 * 24 * 14; // 14 days
234
+
235
+ const _profilesV2 = new Map();
236
+ const _observationsV2 = new Map();
237
+ let _maxActiveProfilesPerUserV2 = INSTINCT_DEFAULT_MAX_ACTIVE_PROFILES_PER_USER;
238
+ let _maxPendingObsPerProfileV2 = INSTINCT_DEFAULT_MAX_PENDING_OBS_PER_PROFILE;
239
+ let _profileIdleMsV2 = INSTINCT_DEFAULT_PROFILE_IDLE_MS;
240
+ let _obsStuckMsV2 = INSTINCT_DEFAULT_OBS_STUCK_MS;
241
+
242
+ function _posIntInstinctV2(n, label) {
243
+ const v = Math.floor(Number(n));
244
+ if (!Number.isFinite(v) || v <= 0)
245
+ throw new Error(`${label} must be a positive integer`);
246
+ return v;
247
+ }
248
+
249
+ export function getMaxActiveProfilesPerUserV2() {
250
+ return _maxActiveProfilesPerUserV2;
251
+ }
252
+ export function setMaxActiveProfilesPerUserV2(n) {
253
+ _maxActiveProfilesPerUserV2 = _posIntInstinctV2(
254
+ n,
255
+ "maxActiveProfilesPerUser",
256
+ );
257
+ }
258
+ export function getMaxPendingObsPerProfileV2() {
259
+ return _maxPendingObsPerProfileV2;
260
+ }
261
+ export function setMaxPendingObsPerProfileV2(n) {
262
+ _maxPendingObsPerProfileV2 = _posIntInstinctV2(n, "maxPendingObsPerProfile");
263
+ }
264
+ export function getProfileIdleMsV2() {
265
+ return _profileIdleMsV2;
266
+ }
267
+ export function setProfileIdleMsV2(n) {
268
+ _profileIdleMsV2 = _posIntInstinctV2(n, "profileIdleMs");
269
+ }
270
+ export function getObsStuckMsV2() {
271
+ return _obsStuckMsV2;
272
+ }
273
+ export function setObsStuckMsV2(n) {
274
+ _obsStuckMsV2 = _posIntInstinctV2(n, "obsStuckMs");
275
+ }
276
+
277
+ export function getActiveProfileCountV2(userId) {
278
+ let n = 0;
279
+ for (const p of _profilesV2.values()) {
280
+ if (p.userId === userId && p.status === "active") n += 1;
281
+ }
282
+ return n;
283
+ }
284
+
285
+ export function getPendingObsCountV2(profileId) {
286
+ let n = 0;
287
+ for (const o of _observationsV2.values()) {
288
+ if (
289
+ o.profileId === profileId &&
290
+ (o.status === "captured" || o.status === "reviewed")
291
+ )
292
+ n += 1;
293
+ }
294
+ return n;
295
+ }
296
+
297
+ function _copyProfileV2(p) {
298
+ return { ...p, metadata: { ...p.metadata } };
299
+ }
300
+ function _copyObsV2(o) {
301
+ return { ...o, metadata: { ...o.metadata } };
302
+ }
303
+
304
+ export function registerProfileV2(
305
+ id,
306
+ { userId, category, metadata = {}, now = Date.now() } = {},
307
+ ) {
308
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
309
+ if (!userId || typeof userId !== "string")
310
+ throw new Error("userId must be a string");
311
+ if (!category || typeof category !== "string")
312
+ throw new Error("category must be a string");
313
+ if (_profilesV2.has(id)) throw new Error(`profile ${id} already exists`);
314
+ const p = {
315
+ id,
316
+ userId,
317
+ category,
318
+ status: "pending",
319
+ createdAt: now,
320
+ lastSeenAt: now,
321
+ activatedAt: null,
322
+ archivedAt: null,
323
+ metadata: { ...metadata },
324
+ };
325
+ _profilesV2.set(id, p);
326
+ return _copyProfileV2(p);
327
+ }
328
+
329
+ export function getProfileV2(id) {
330
+ const p = _profilesV2.get(id);
331
+ return p ? _copyProfileV2(p) : null;
332
+ }
333
+
334
+ export function listProfilesV2({ userId, category, status } = {}) {
335
+ const out = [];
336
+ for (const p of _profilesV2.values()) {
337
+ if (userId && p.userId !== userId) continue;
338
+ if (category && p.category !== category) continue;
339
+ if (status && p.status !== status) continue;
340
+ out.push(_copyProfileV2(p));
341
+ }
342
+ return out;
343
+ }
344
+
345
+ export function setProfileStatusV2(id, next, { now = Date.now() } = {}) {
346
+ const p = _profilesV2.get(id);
347
+ if (!p) throw new Error(`profile ${id} not found`);
348
+ if (!PROFILE_TRANSITIONS_V2.has(next))
349
+ throw new Error(`unknown profile status: ${next}`);
350
+ if (PROFILE_TERMINALS_V2.has(p.status))
351
+ throw new Error(`profile ${id} is in terminal state ${p.status}`);
352
+ const allowed = PROFILE_TRANSITIONS_V2.get(p.status);
353
+ if (!allowed.has(next))
354
+ throw new Error(`cannot transition profile from ${p.status} to ${next}`);
355
+ if (next === "active") {
356
+ if (p.status === "pending") {
357
+ const count = getActiveProfileCountV2(p.userId);
358
+ if (count >= _maxActiveProfilesPerUserV2)
359
+ throw new Error(
360
+ `user ${p.userId} already at active-profile cap (${_maxActiveProfilesPerUserV2})`,
361
+ );
362
+ }
363
+ if (!p.activatedAt) p.activatedAt = now;
364
+ }
365
+ if (next === "archived" && !p.archivedAt) p.archivedAt = now;
366
+ p.status = next;
367
+ p.lastSeenAt = now;
368
+ return _copyProfileV2(p);
369
+ }
370
+
371
+ export function activateProfileV2(id, opts) {
372
+ return setProfileStatusV2(id, "active", opts);
373
+ }
374
+ export function dormantProfileV2(id, opts) {
375
+ return setProfileStatusV2(id, "dormant", opts);
376
+ }
377
+ export function archiveProfileV2(id, opts) {
378
+ return setProfileStatusV2(id, "archived", opts);
379
+ }
380
+
381
+ export function touchProfileV2(id, { now = Date.now() } = {}) {
382
+ const p = _profilesV2.get(id);
383
+ if (!p) throw new Error(`profile ${id} not found`);
384
+ p.lastSeenAt = now;
385
+ return _copyProfileV2(p);
386
+ }
387
+
388
+ export function createObservationV2(
389
+ id,
390
+ { profileId, signal, metadata = {}, now = Date.now() } = {},
391
+ ) {
392
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
393
+ if (!profileId || typeof profileId !== "string")
394
+ throw new Error("profileId must be a string");
395
+ if (!signal || typeof signal !== "string")
396
+ throw new Error("signal must be a string");
397
+ if (_observationsV2.has(id))
398
+ throw new Error(`observation ${id} already exists`);
399
+ const count = getPendingObsCountV2(profileId);
400
+ if (count >= _maxPendingObsPerProfileV2)
401
+ throw new Error(
402
+ `profile ${profileId} already at pending-obs cap (${_maxPendingObsPerProfileV2})`,
403
+ );
404
+ const o = {
405
+ id,
406
+ profileId,
407
+ signal,
408
+ status: "captured",
409
+ createdAt: now,
410
+ lastSeenAt: now,
411
+ reviewedAt: null,
412
+ settledAt: null,
413
+ metadata: { ...metadata },
414
+ };
415
+ _observationsV2.set(id, o);
416
+ return _copyObsV2(o);
417
+ }
418
+
419
+ export function getObservationV2(id) {
420
+ const o = _observationsV2.get(id);
421
+ return o ? _copyObsV2(o) : null;
422
+ }
423
+
424
+ export function listObservationsV2({ profileId, status } = {}) {
425
+ const out = [];
426
+ for (const o of _observationsV2.values()) {
427
+ if (profileId && o.profileId !== profileId) continue;
428
+ if (status && o.status !== status) continue;
429
+ out.push(_copyObsV2(o));
430
+ }
431
+ return out;
432
+ }
433
+
434
+ export function setObservationStatusV2(id, next, { now = Date.now() } = {}) {
435
+ const o = _observationsV2.get(id);
436
+ if (!o) throw new Error(`observation ${id} not found`);
437
+ if (!OBS_TRANSITIONS_V2.has(next))
438
+ throw new Error(`unknown observation status: ${next}`);
439
+ if (OBS_TERMINALS_V2.has(o.status))
440
+ throw new Error(`observation ${id} is in terminal state ${o.status}`);
441
+ const allowed = OBS_TRANSITIONS_V2.get(o.status);
442
+ if (!allowed.has(next))
443
+ throw new Error(
444
+ `cannot transition observation from ${o.status} to ${next}`,
445
+ );
446
+ if (next === "reviewed" && !o.reviewedAt) o.reviewedAt = now;
447
+ if (OBS_TERMINALS_V2.has(next) && !o.settledAt) o.settledAt = now;
448
+ o.status = next;
449
+ o.lastSeenAt = now;
450
+ return _copyObsV2(o);
451
+ }
452
+
453
+ export function reviewObservationV2(id, opts) {
454
+ return setObservationStatusV2(id, "reviewed", opts);
455
+ }
456
+ export function reinforceObservationV2(id, opts) {
457
+ return setObservationStatusV2(id, "reinforced", opts);
458
+ }
459
+ export function promoteObservationV2(id, opts) {
460
+ return setObservationStatusV2(id, "promoted", opts);
461
+ }
462
+ export function discardObservationV2(id, opts) {
463
+ return setObservationStatusV2(id, "discarded", opts);
464
+ }
465
+
466
+ export function autoDormantIdleProfilesV2({ now = Date.now() } = {}) {
467
+ const flipped = [];
468
+ for (const p of _profilesV2.values()) {
469
+ if (p.status !== "active") continue;
470
+ if (now - p.lastSeenAt > _profileIdleMsV2) {
471
+ p.status = "dormant";
472
+ p.lastSeenAt = now;
473
+ flipped.push(_copyProfileV2(p));
474
+ }
475
+ }
476
+ return flipped;
477
+ }
478
+
479
+ export function autoDiscardStaleObservationsV2({ now = Date.now() } = {}) {
480
+ const flipped = [];
481
+ for (const o of _observationsV2.values()) {
482
+ if (o.status !== "captured" && o.status !== "reviewed") continue;
483
+ if (now - o.lastSeenAt > _obsStuckMsV2) {
484
+ o.status = "discarded";
485
+ o.lastSeenAt = now;
486
+ if (!o.settledAt) o.settledAt = now;
487
+ flipped.push(_copyObsV2(o));
488
+ }
489
+ }
490
+ return flipped;
491
+ }
492
+
493
+ export function getInstinctManagerStatsV2() {
494
+ const profilesByStatus = {};
495
+ for (const v of Object.values(PROFILE_MATURITY_V2)) profilesByStatus[v] = 0;
496
+ for (const p of _profilesV2.values()) profilesByStatus[p.status] += 1;
497
+
498
+ const observationsByStatus = {};
499
+ for (const v of Object.values(OBSERVATION_LIFECYCLE_V2))
500
+ observationsByStatus[v] = 0;
501
+ for (const o of _observationsV2.values()) observationsByStatus[o.status] += 1;
502
+
503
+ return {
504
+ totalProfilesV2: _profilesV2.size,
505
+ totalObservationsV2: _observationsV2.size,
506
+ maxActiveProfilesPerUser: _maxActiveProfilesPerUserV2,
507
+ maxPendingObsPerProfile: _maxPendingObsPerProfileV2,
508
+ profileIdleMs: _profileIdleMsV2,
509
+ obsStuckMs: _obsStuckMsV2,
510
+ profilesByStatus,
511
+ observationsByStatus,
512
+ };
513
+ }
514
+
515
+ export function _resetStateInstinctManagerV2() {
516
+ _profilesV2.clear();
517
+ _observationsV2.clear();
518
+ _maxActiveProfilesPerUserV2 = INSTINCT_DEFAULT_MAX_ACTIVE_PROFILES_PER_USER;
519
+ _maxPendingObsPerProfileV2 = INSTINCT_DEFAULT_MAX_PENDING_OBS_PER_PROFILE;
520
+ _profileIdleMsV2 = INSTINCT_DEFAULT_PROFILE_IDLE_MS;
521
+ _obsStuckMsV2 = INSTINCT_DEFAULT_OBS_STUCK_MS;
522
+ }