@soleri/core 2.1.0 → 2.4.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.
- package/dist/brain/brain.d.ts +3 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +60 -4
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts +36 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +119 -14
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +32 -0
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/control/identity-manager.d.ts +22 -0
- package/dist/control/identity-manager.d.ts.map +1 -0
- package/dist/control/identity-manager.js +233 -0
- package/dist/control/identity-manager.js.map +1 -0
- package/dist/control/intent-router.d.ts +32 -0
- package/dist/control/intent-router.d.ts.map +1 -0
- package/dist/control/intent-router.js +242 -0
- package/dist/control/intent-router.js.map +1 -0
- package/dist/control/types.d.ts +68 -0
- package/dist/control/types.d.ts.map +1 -0
- package/dist/control/types.js +9 -0
- package/dist/control/types.js.map +1 -0
- package/dist/curator/curator.d.ts +29 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +135 -0
- package/dist/curator/curator.js.map +1 -1
- package/dist/facades/types.d.ts +1 -1
- package/dist/governance/governance.d.ts +42 -0
- package/dist/governance/governance.d.ts.map +1 -0
- package/dist/governance/governance.js +488 -0
- package/dist/governance/governance.js.map +1 -0
- package/dist/governance/index.d.ts +3 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/governance/types.d.ts +102 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/governance/types.js +3 -0
- package/dist/governance/types.js.map +1 -0
- package/dist/index.d.ts +32 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.d.ts +37 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +145 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/types.d.ts +19 -0
- package/dist/logging/types.d.ts.map +1 -0
- package/dist/logging/types.js +2 -0
- package/dist/logging/types.js.map +1 -0
- package/dist/loop/loop-manager.d.ts +49 -0
- package/dist/loop/loop-manager.d.ts.map +1 -0
- package/dist/loop/loop-manager.js +105 -0
- package/dist/loop/loop-manager.js.map +1 -0
- package/dist/loop/types.d.ts +35 -0
- package/dist/loop/types.d.ts.map +1 -0
- package/dist/loop/types.js +8 -0
- package/dist/loop/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +29 -0
- package/dist/planning/gap-analysis.d.ts.map +1 -0
- package/dist/planning/gap-analysis.js +265 -0
- package/dist/planning/gap-analysis.js.map +1 -0
- package/dist/planning/gap-types.d.ts +29 -0
- package/dist/planning/gap-types.d.ts.map +1 -0
- package/dist/planning/gap-types.js +28 -0
- package/dist/planning/gap-types.js.map +1 -0
- package/dist/planning/planner.d.ts +150 -1
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +365 -2
- package/dist/planning/planner.js.map +1 -1
- package/dist/project/project-registry.d.ts +79 -0
- package/dist/project/project-registry.d.ts.map +1 -0
- package/dist/project/project-registry.js +276 -0
- package/dist/project/project-registry.js.map +1 -0
- package/dist/project/types.d.ts +28 -0
- package/dist/project/types.d.ts.map +1 -0
- package/dist/project/types.js +5 -0
- package/dist/project/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +13 -0
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -0
- package/dist/runtime/admin-extra-ops.js +284 -0
- package/dist/runtime/admin-extra-ops.js.map +1 -0
- package/dist/runtime/admin-ops.d.ts +15 -0
- package/dist/runtime/admin-ops.d.ts.map +1 -0
- package/dist/runtime/admin-ops.js +322 -0
- package/dist/runtime/admin-ops.js.map +1 -0
- package/dist/runtime/capture-ops.d.ts +15 -0
- package/dist/runtime/capture-ops.d.ts.map +1 -0
- package/dist/runtime/capture-ops.js +345 -0
- package/dist/runtime/capture-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +7 -3
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +474 -8
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +9 -0
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -0
- package/dist/runtime/curator-extra-ops.js +59 -0
- package/dist/runtime/curator-extra-ops.js.map +1 -0
- package/dist/runtime/domain-ops.d.ts.map +1 -1
- package/dist/runtime/domain-ops.js +59 -13
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts +14 -0
- package/dist/runtime/grading-ops.d.ts.map +1 -0
- package/dist/runtime/grading-ops.js +105 -0
- package/dist/runtime/grading-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +13 -0
- package/dist/runtime/loop-ops.d.ts.map +1 -0
- package/dist/runtime/loop-ops.js +179 -0
- package/dist/runtime/loop-ops.js.map +1 -0
- package/dist/runtime/memory-cross-project-ops.d.ts +12 -0
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -0
- package/dist/runtime/memory-cross-project-ops.js +165 -0
- package/dist/runtime/memory-cross-project-ops.js.map +1 -0
- package/dist/runtime/memory-extra-ops.d.ts +13 -0
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -0
- package/dist/runtime/memory-extra-ops.js +173 -0
- package/dist/runtime/memory-extra-ops.js.map +1 -0
- package/dist/runtime/orchestrate-ops.d.ts +17 -0
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -0
- package/dist/runtime/orchestrate-ops.js +240 -0
- package/dist/runtime/orchestrate-ops.js.map +1 -0
- package/dist/runtime/planning-extra-ops.d.ts +17 -0
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -0
- package/dist/runtime/planning-extra-ops.js +300 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts +15 -0
- package/dist/runtime/project-ops.d.ts.map +1 -0
- package/dist/runtime/project-ops.js +181 -0
- package/dist/runtime/project-ops.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +44 -1
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +21 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +9 -0
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -0
- package/dist/runtime/vault-extra-ops.js +195 -0
- package/dist/runtime/vault-extra-ops.js.map +1 -0
- package/dist/telemetry/telemetry.d.ts +48 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -0
- package/dist/telemetry/telemetry.js +87 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/vault/vault.d.ts +94 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +340 -1
- package/dist/vault/vault.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/admin-extra-ops.test.ts +420 -0
- package/src/__tests__/admin-ops.test.ts +271 -0
- package/src/__tests__/brain-intelligence.test.ts +205 -0
- package/src/__tests__/brain.test.ts +131 -0
- package/src/__tests__/capture-ops.test.ts +509 -0
- package/src/__tests__/core-ops.test.ts +266 -2
- package/src/__tests__/curator-extra-ops.test.ts +359 -0
- package/src/__tests__/domain-ops.test.ts +66 -0
- package/src/__tests__/governance.test.ts +522 -0
- package/src/__tests__/grading-ops.test.ts +340 -0
- package/src/__tests__/identity-manager.test.ts +243 -0
- package/src/__tests__/intent-router.test.ts +222 -0
- package/src/__tests__/logger.test.ts +200 -0
- package/src/__tests__/loop-ops.test.ts +398 -0
- package/src/__tests__/memory-cross-project-ops.test.ts +246 -0
- package/src/__tests__/memory-extra-ops.test.ts +352 -0
- package/src/__tests__/orchestrate-ops.test.ts +284 -0
- package/src/__tests__/planner.test.ts +331 -0
- package/src/__tests__/planning-extra-ops.test.ts +548 -0
- package/src/__tests__/project-ops.test.ts +367 -0
- package/src/__tests__/vault-extra-ops.test.ts +407 -0
- package/src/brain/brain.ts +114 -7
- package/src/brain/intelligence.ts +179 -10
- package/src/brain/types.ts +38 -0
- package/src/control/identity-manager.ts +354 -0
- package/src/control/intent-router.ts +326 -0
- package/src/control/types.ts +102 -0
- package/src/curator/curator.ts +213 -0
- package/src/governance/governance.ts +698 -0
- package/src/governance/index.ts +18 -0
- package/src/governance/types.ts +111 -0
- package/src/index.ts +102 -2
- package/src/logging/logger.ts +154 -0
- package/src/logging/types.ts +21 -0
- package/src/loop/loop-manager.ts +130 -0
- package/src/loop/types.ts +44 -0
- package/src/planning/gap-analysis.ts +506 -0
- package/src/planning/gap-types.ts +58 -0
- package/src/planning/planner.ts +478 -2
- package/src/project/project-registry.ts +358 -0
- package/src/project/types.ts +31 -0
- package/src/runtime/admin-extra-ops.ts +307 -0
- package/src/runtime/admin-ops.ts +329 -0
- package/src/runtime/capture-ops.ts +385 -0
- package/src/runtime/core-ops.ts +535 -7
- package/src/runtime/curator-extra-ops.ts +71 -0
- package/src/runtime/domain-ops.ts +65 -13
- package/src/runtime/grading-ops.ts +121 -0
- package/src/runtime/loop-ops.ts +194 -0
- package/src/runtime/memory-cross-project-ops.ts +192 -0
- package/src/runtime/memory-extra-ops.ts +186 -0
- package/src/runtime/orchestrate-ops.ts +272 -0
- package/src/runtime/planning-extra-ops.ts +327 -0
- package/src/runtime/project-ops.ts +196 -0
- package/src/runtime/runtime.ts +49 -1
- package/src/runtime/types.ts +21 -0
- package/src/runtime/vault-extra-ops.ts +225 -0
- package/src/telemetry/telemetry.ts +118 -0
- package/src/vault/vault.ts +412 -1
|
@@ -431,6 +431,41 @@ describe('BrainIntelligence', () => {
|
|
|
431
431
|
expect(result.failed).toContain('non-existent');
|
|
432
432
|
});
|
|
433
433
|
|
|
434
|
+
it('should gate promote through governance when strict preset', () => {
|
|
435
|
+
// Create a brain session and extract proposals
|
|
436
|
+
runtime.brainIntelligence.lifecycle({
|
|
437
|
+
action: 'start',
|
|
438
|
+
sessionId: 'gov-promo-1',
|
|
439
|
+
toolsUsed: ['w', 'w', 'w'],
|
|
440
|
+
});
|
|
441
|
+
runtime.brainIntelligence.lifecycle({ action: 'end', sessionId: 'gov-promo-1' });
|
|
442
|
+
runtime.brainIntelligence.extractKnowledge('gov-promo-1');
|
|
443
|
+
|
|
444
|
+
const proposals = runtime.brainIntelligence.getProposals({ sessionId: 'gov-promo-1' });
|
|
445
|
+
expect(proposals.length).toBeGreaterThan(0);
|
|
446
|
+
|
|
447
|
+
// Apply strict preset — requireReview: true
|
|
448
|
+
runtime.governance.applyPreset('.', 'strict', 'test');
|
|
449
|
+
|
|
450
|
+
const result = runtime.brainIntelligence.promoteProposals(
|
|
451
|
+
[proposals[0].id],
|
|
452
|
+
runtime.governance,
|
|
453
|
+
'.',
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// Should be gated, not promoted
|
|
457
|
+
expect(result.promoted).toBe(0);
|
|
458
|
+
expect(result.gated.length).toBe(1);
|
|
459
|
+
expect(result.gated[0].action).toBe('propose');
|
|
460
|
+
|
|
461
|
+
// Entry should NOT be in vault
|
|
462
|
+
expect(runtime.vault.get(`proposal-${proposals[0].id}`)).toBeNull();
|
|
463
|
+
|
|
464
|
+
// But should be in governance proposals
|
|
465
|
+
const pending = runtime.governance.listPendingProposals('.');
|
|
466
|
+
expect(pending.some((p) => p.source === 'brain-promote')).toBe(true);
|
|
467
|
+
});
|
|
468
|
+
|
|
434
469
|
// ─── Build Intelligence ────────────────────────────────────────
|
|
435
470
|
|
|
436
471
|
it('should build intelligence pipeline', () => {
|
|
@@ -557,6 +592,7 @@ describe('BrainIntelligence', () => {
|
|
|
557
592
|
filesModified: [],
|
|
558
593
|
planId: null,
|
|
559
594
|
planOutcome: null,
|
|
595
|
+
extractedAt: null,
|
|
560
596
|
},
|
|
561
597
|
],
|
|
562
598
|
proposals: [],
|
|
@@ -609,6 +645,7 @@ describe('BrainIntelligence', () => {
|
|
|
609
645
|
filesModified: [],
|
|
610
646
|
planId: null,
|
|
611
647
|
planOutcome: null,
|
|
648
|
+
extractedAt: null,
|
|
612
649
|
},
|
|
613
650
|
],
|
|
614
651
|
proposals: [],
|
|
@@ -620,4 +657,172 @@ describe('BrainIntelligence', () => {
|
|
|
620
657
|
const result = runtime.brainIntelligence.importData(data);
|
|
621
658
|
expect(result.imported.sessions).toBe(0); // Duplicate ignored
|
|
622
659
|
});
|
|
660
|
+
|
|
661
|
+
// ─── Auto-extraction on session end ─────────────────────────
|
|
662
|
+
|
|
663
|
+
it('should auto-extract when session ends with tools used', () => {
|
|
664
|
+
runtime.brainIntelligence.lifecycle({
|
|
665
|
+
action: 'start',
|
|
666
|
+
sessionId: 'auto-1',
|
|
667
|
+
domain: 'testing',
|
|
668
|
+
toolsUsed: ['search', 'search', 'search'],
|
|
669
|
+
});
|
|
670
|
+
const session = runtime.brainIntelligence.lifecycle({
|
|
671
|
+
action: 'end',
|
|
672
|
+
sessionId: 'auto-1',
|
|
673
|
+
});
|
|
674
|
+
// extractedAt should be set automatically
|
|
675
|
+
expect(session.extractedAt).not.toBeNull();
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('should not auto-extract when session has no signal', () => {
|
|
679
|
+
runtime.brainIntelligence.lifecycle({
|
|
680
|
+
action: 'start',
|
|
681
|
+
sessionId: 'auto-2',
|
|
682
|
+
});
|
|
683
|
+
const session = runtime.brainIntelligence.lifecycle({
|
|
684
|
+
action: 'end',
|
|
685
|
+
sessionId: 'auto-2',
|
|
686
|
+
});
|
|
687
|
+
// No tools, no files, no plan — should not extract
|
|
688
|
+
expect(session.extractedAt).toBeNull();
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
it('should auto-extract when session has a plan', () => {
|
|
692
|
+
runtime.brainIntelligence.lifecycle({
|
|
693
|
+
action: 'start',
|
|
694
|
+
sessionId: 'auto-3',
|
|
695
|
+
planId: 'plan-123',
|
|
696
|
+
});
|
|
697
|
+
const session = runtime.brainIntelligence.lifecycle({
|
|
698
|
+
action: 'end',
|
|
699
|
+
sessionId: 'auto-3',
|
|
700
|
+
planOutcome: 'completed',
|
|
701
|
+
});
|
|
702
|
+
expect(session.extractedAt).not.toBeNull();
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
// ─── extractedAt Tracking ──────────────────────────────────
|
|
706
|
+
|
|
707
|
+
it('should set extractedAt when extractKnowledge is called manually', () => {
|
|
708
|
+
// Use a session with no tools/files/plan so auto-extraction doesn't fire
|
|
709
|
+
runtime.brainIntelligence.lifecycle({
|
|
710
|
+
action: 'start',
|
|
711
|
+
sessionId: 'ext-1',
|
|
712
|
+
domain: 'testing',
|
|
713
|
+
});
|
|
714
|
+
runtime.brainIntelligence.lifecycle({
|
|
715
|
+
action: 'end',
|
|
716
|
+
sessionId: 'ext-1',
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
// Before manual extraction, extractedAt should be null (no auto-extract — no signal)
|
|
720
|
+
const ctx = runtime.brainIntelligence.getSessionContext(10);
|
|
721
|
+
const before = ctx.recentSessions.find((s) => s.id === 'ext-1');
|
|
722
|
+
expect(before?.extractedAt).toBeNull();
|
|
723
|
+
|
|
724
|
+
// Manual extract
|
|
725
|
+
runtime.brainIntelligence.extractKnowledge('ext-1');
|
|
726
|
+
|
|
727
|
+
// After extraction, extractedAt should be set
|
|
728
|
+
const ctx2 = runtime.brainIntelligence.getSessionContext(10);
|
|
729
|
+
const after = ctx2.recentSessions.find((s) => s.id === 'ext-1');
|
|
730
|
+
expect(after?.extractedAt).not.toBeNull();
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// ─── resetExtracted ────────────────────────────────────────
|
|
734
|
+
|
|
735
|
+
it('should reset extractedAt for a specific session', () => {
|
|
736
|
+
runtime.brainIntelligence.lifecycle({
|
|
737
|
+
action: 'start',
|
|
738
|
+
sessionId: 'reset-1',
|
|
739
|
+
toolsUsed: ['a', 'a', 'a'],
|
|
740
|
+
});
|
|
741
|
+
runtime.brainIntelligence.lifecycle({ action: 'end', sessionId: 'reset-1' });
|
|
742
|
+
runtime.brainIntelligence.extractKnowledge('reset-1');
|
|
743
|
+
|
|
744
|
+
const result = runtime.brainIntelligence.resetExtracted({ sessionId: 'reset-1' });
|
|
745
|
+
expect(result.reset).toBe(1);
|
|
746
|
+
|
|
747
|
+
const ctx = runtime.brainIntelligence.getSessionContext(10);
|
|
748
|
+
const session = ctx.recentSessions.find((s) => s.id === 'reset-1');
|
|
749
|
+
expect(session?.extractedAt).toBeNull();
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it('should reset all extracted sessions', () => {
|
|
753
|
+
runtime.brainIntelligence.lifecycle({
|
|
754
|
+
action: 'start',
|
|
755
|
+
sessionId: 'reset-a',
|
|
756
|
+
toolsUsed: ['a', 'a', 'a'],
|
|
757
|
+
});
|
|
758
|
+
runtime.brainIntelligence.lifecycle({ action: 'end', sessionId: 'reset-a' });
|
|
759
|
+
runtime.brainIntelligence.extractKnowledge('reset-a');
|
|
760
|
+
|
|
761
|
+
runtime.brainIntelligence.lifecycle({
|
|
762
|
+
action: 'start',
|
|
763
|
+
sessionId: 'reset-b',
|
|
764
|
+
toolsUsed: ['b', 'b', 'b'],
|
|
765
|
+
});
|
|
766
|
+
runtime.brainIntelligence.lifecycle({ action: 'end', sessionId: 'reset-b' });
|
|
767
|
+
runtime.brainIntelligence.extractKnowledge('reset-b');
|
|
768
|
+
|
|
769
|
+
const result = runtime.brainIntelligence.resetExtracted({ all: true });
|
|
770
|
+
expect(result.reset).toBe(2);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('should return 0 when nothing to reset', () => {
|
|
774
|
+
const result = runtime.brainIntelligence.resetExtracted({ all: true });
|
|
775
|
+
expect(result.reset).toBe(0);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
it('should return 0 when no filter is provided', () => {
|
|
779
|
+
const result = runtime.brainIntelligence.resetExtracted();
|
|
780
|
+
expect(result.reset).toBe(0);
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
it('should allow re-extraction after reset', () => {
|
|
784
|
+
runtime.brainIntelligence.lifecycle({
|
|
785
|
+
action: 'start',
|
|
786
|
+
sessionId: 'reext-1',
|
|
787
|
+
toolsUsed: ['search', 'search', 'search'],
|
|
788
|
+
});
|
|
789
|
+
runtime.brainIntelligence.lifecycle({ action: 'end', sessionId: 'reext-1' });
|
|
790
|
+
|
|
791
|
+
const first = runtime.brainIntelligence.extractKnowledge('reext-1');
|
|
792
|
+
expect(first.proposals.length).toBeGreaterThan(0);
|
|
793
|
+
|
|
794
|
+
runtime.brainIntelligence.resetExtracted({ sessionId: 'reext-1' });
|
|
795
|
+
|
|
796
|
+
const second = runtime.brainIntelligence.extractKnowledge('reext-1');
|
|
797
|
+
expect(second.proposals.length).toBeGreaterThan(0);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
// ─── computeStrengths with modified feedback ───────────────
|
|
801
|
+
|
|
802
|
+
it('should weight modified feedback at 0.5 in computeStrengths', () => {
|
|
803
|
+
// Seed an entry for feedback to reference
|
|
804
|
+
runtime.vault.seed([
|
|
805
|
+
{
|
|
806
|
+
id: 'str-1',
|
|
807
|
+
type: 'pattern',
|
|
808
|
+
domain: 'testing',
|
|
809
|
+
title: 'Strength test pattern',
|
|
810
|
+
severity: 'warning',
|
|
811
|
+
description: 'Testing strength computation with modified feedback.',
|
|
812
|
+
tags: ['test'],
|
|
813
|
+
},
|
|
814
|
+
]);
|
|
815
|
+
|
|
816
|
+
// Record feedback: 1 accepted, 1 modified, 1 failed
|
|
817
|
+
runtime.brain.recordFeedback({ query: 'q1', entryId: 'str-1', action: 'accepted' });
|
|
818
|
+
runtime.brain.recordFeedback({ query: 'q2', entryId: 'str-1', action: 'modified' });
|
|
819
|
+
runtime.brain.recordFeedback({ query: 'q3', entryId: 'str-1', action: 'failed' });
|
|
820
|
+
|
|
821
|
+
const strengths = runtime.brainIntelligence.computeStrengths();
|
|
822
|
+
const s = strengths.find((p) => p.pattern === 'Strength test pattern');
|
|
823
|
+
expect(s).toBeDefined();
|
|
824
|
+
|
|
825
|
+
// successRate = (1 + 0.5) / (3 - 1) = 1.5/2 = 0.75
|
|
826
|
+
expect(s!.successRate).toBeCloseTo(0.75, 2);
|
|
827
|
+
});
|
|
623
828
|
});
|
|
@@ -741,6 +741,137 @@ describe('Brain', () => {
|
|
|
741
741
|
});
|
|
742
742
|
});
|
|
743
743
|
|
|
744
|
+
// ─── Enhanced Feedback ─────────────────────────────────────
|
|
745
|
+
|
|
746
|
+
describe('enhanced feedback', () => {
|
|
747
|
+
beforeEach(() => {
|
|
748
|
+
vault.seed([makeEntry({ id: 'fb-1', title: 'Feedback test pattern', tags: ['feedback'] })]);
|
|
749
|
+
brain = new Brain(vault);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it('should accept legacy 3-arg form (backward compat)', () => {
|
|
753
|
+
brain.recordFeedback('test query', 'fb-1', 'accepted');
|
|
754
|
+
const stats = brain.getFeedbackStats();
|
|
755
|
+
expect(stats.total).toBe(1);
|
|
756
|
+
expect(stats.byAction['accepted']).toBe(1);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
it('should accept FeedbackInput and return FeedbackEntry', () => {
|
|
760
|
+
const entry = brain.recordFeedback({
|
|
761
|
+
query: 'test query',
|
|
762
|
+
entryId: 'fb-1',
|
|
763
|
+
action: 'modified',
|
|
764
|
+
source: 'recommendation',
|
|
765
|
+
confidence: 0.85,
|
|
766
|
+
duration: 1200,
|
|
767
|
+
reason: 'adjusted wording',
|
|
768
|
+
});
|
|
769
|
+
expect(entry).toBeDefined();
|
|
770
|
+
expect(entry.action).toBe('modified');
|
|
771
|
+
expect(entry.source).toBe('recommendation');
|
|
772
|
+
expect(entry.confidence).toBe(0.85);
|
|
773
|
+
expect(entry.duration).toBe(1200);
|
|
774
|
+
expect(entry.reason).toBe('adjusted wording');
|
|
775
|
+
expect(entry.id).toBeGreaterThan(0);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
it('should accept modified and failed action types', () => {
|
|
779
|
+
brain.recordFeedback({ query: 'q1', entryId: 'fb-1', action: 'modified' });
|
|
780
|
+
brain.recordFeedback({ query: 'q2', entryId: 'fb-1', action: 'failed' });
|
|
781
|
+
const stats = brain.getFeedbackStats();
|
|
782
|
+
expect(stats.total).toBe(2);
|
|
783
|
+
expect(stats.byAction['modified']).toBe(1);
|
|
784
|
+
expect(stats.byAction['failed']).toBe(1);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
it('should use default source and confidence when not provided', () => {
|
|
788
|
+
const entry = brain.recordFeedback({ query: 'q1', entryId: 'fb-1', action: 'accepted' });
|
|
789
|
+
expect(entry.source).toBe('search');
|
|
790
|
+
expect(entry.confidence).toBe(0.6);
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// ─── Feedback Stats ───────────────────────────────────────
|
|
795
|
+
|
|
796
|
+
describe('getFeedbackStats', () => {
|
|
797
|
+
beforeEach(() => {
|
|
798
|
+
vault.seed([makeEntry({ id: 'fs-1', tags: ['stats'] })]);
|
|
799
|
+
brain = new Brain(vault);
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it('should return zero stats on empty feedback', () => {
|
|
803
|
+
const stats = brain.getFeedbackStats();
|
|
804
|
+
expect(stats.total).toBe(0);
|
|
805
|
+
expect(stats.acceptanceRate).toBe(0);
|
|
806
|
+
expect(stats.averageConfidence).toBe(0);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it('should compute acceptance rate correctly', () => {
|
|
810
|
+
brain.recordFeedback('q1', 'fs-1', 'accepted');
|
|
811
|
+
brain.recordFeedback('q2', 'fs-1', 'dismissed');
|
|
812
|
+
brain.recordFeedback('q3', 'fs-1', 'accepted');
|
|
813
|
+
const stats = brain.getFeedbackStats();
|
|
814
|
+
expect(stats.total).toBe(3);
|
|
815
|
+
expect(stats.acceptanceRate).toBeCloseTo(2 / 3, 2);
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
it('should group by action and source', () => {
|
|
819
|
+
brain.recordFeedback({ query: 'q1', entryId: 'fs-1', action: 'accepted', source: 'search' });
|
|
820
|
+
brain.recordFeedback({
|
|
821
|
+
query: 'q2',
|
|
822
|
+
entryId: 'fs-1',
|
|
823
|
+
action: 'modified',
|
|
824
|
+
source: 'recommendation',
|
|
825
|
+
});
|
|
826
|
+
brain.recordFeedback({
|
|
827
|
+
query: 'q3',
|
|
828
|
+
entryId: 'fs-1',
|
|
829
|
+
action: 'failed',
|
|
830
|
+
source: 'tool-execution',
|
|
831
|
+
});
|
|
832
|
+
const stats = brain.getFeedbackStats();
|
|
833
|
+
expect(stats.byAction['accepted']).toBe(1);
|
|
834
|
+
expect(stats.byAction['modified']).toBe(1);
|
|
835
|
+
expect(stats.byAction['failed']).toBe(1);
|
|
836
|
+
expect(stats.bySource['search']).toBe(1);
|
|
837
|
+
expect(stats.bySource['recommendation']).toBe(1);
|
|
838
|
+
expect(stats.bySource['tool-execution']).toBe(1);
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
it('should compute average confidence', () => {
|
|
842
|
+
brain.recordFeedback({ query: 'q1', entryId: 'fs-1', action: 'accepted', confidence: 0.9 });
|
|
843
|
+
brain.recordFeedback({ query: 'q2', entryId: 'fs-1', action: 'dismissed', confidence: 0.3 });
|
|
844
|
+
const stats = brain.getFeedbackStats();
|
|
845
|
+
expect(stats.averageConfidence).toBeCloseTo(0.6, 2);
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// ─── Recompute Weights with Modified/Failed ──────────────
|
|
850
|
+
|
|
851
|
+
describe('recomputeWeights with modified/failed', () => {
|
|
852
|
+
beforeEach(() => {
|
|
853
|
+
vault.seed([makeEntry({ id: 'rw-1', tags: ['weights'] })]);
|
|
854
|
+
brain = new Brain(vault);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
it('should exclude failed from weight computation', () => {
|
|
858
|
+
// Add enough feedback to exceed threshold (30)
|
|
859
|
+
for (let i = 0; i < 20; i++) {
|
|
860
|
+
brain.recordFeedback('q', 'rw-1', 'accepted');
|
|
861
|
+
}
|
|
862
|
+
for (let i = 0; i < 10; i++) {
|
|
863
|
+
brain.recordFeedback({ query: 'q', entryId: 'rw-1', action: 'failed' });
|
|
864
|
+
}
|
|
865
|
+
// Failed entries should not count toward total for weight adaptation
|
|
866
|
+
// 20 accepted out of 20 relevant = 100% accept rate
|
|
867
|
+
const stats = brain.getStats();
|
|
868
|
+
// Weights should have adapted since we have 30+ total but only 20 non-failed
|
|
869
|
+
// (threshold is 30, total is 30, but only 20 are non-failed so threshold not met)
|
|
870
|
+
// The recomputeWeights() counts non-failed, which is 20 < 30, so weights stay default
|
|
871
|
+
expect(stats.weights.semantic).toBeCloseTo(0.4, 2);
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
|
|
744
875
|
// ─── Graceful Degradation ───────────────────────────────────
|
|
745
876
|
|
|
746
877
|
describe('graceful degradation', () => {
|