@neuroverseos/governance 0.7.0 → 0.8.1
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/README.md +95 -4
- package/dist/{chunk-T6EQ7ZBG.js → chunk-ETDIEVAX.js} +880 -134
- package/dist/{chunk-VGFDMPVB.js → chunk-F2LWMOM5.js} +283 -1
- package/dist/cli/neuroverse.cjs +1599 -360
- package/dist/cli/radiant.cjs +1190 -227
- package/dist/cli/radiant.js +25 -7
- package/dist/cli/worldmodel.cjs +300 -21
- package/dist/cli/worldmodel.js +76 -1
- package/dist/{lenses-K5FVSALR.js → lenses-YDMKSXDL.js} +5 -3
- package/dist/radiant/index.cjs +1183 -138
- package/dist/radiant/index.d.cts +382 -23
- package/dist/radiant/index.d.ts +382 -23
- package/dist/radiant/index.js +37 -3
- package/dist/{server-BXMC5NOE.js → server-ZSQ6DRSN.js} +2 -2
- package/dist/worldmodel-create-5SWHVNMQ.js +195 -0
- package/package.json +1 -1
package/dist/radiant/index.cjs
CHANGED
|
@@ -40,16 +40,30 @@ __export(radiant_exports, {
|
|
|
40
40
|
classifyActorDomain: () => classifyActorDomain,
|
|
41
41
|
classifyEvents: () => classifyEvents,
|
|
42
42
|
composeSystemPrompt: () => composeSystemPrompt,
|
|
43
|
+
compressExocortex: () => compressExocortex,
|
|
44
|
+
compressLens: () => compressLens,
|
|
45
|
+
compressPriorReads: () => compressPriorReads,
|
|
46
|
+
compressWorldmodel: () => compressWorldmodel,
|
|
43
47
|
computePersistence: () => computePersistence,
|
|
44
48
|
createAnthropicAI: () => createAnthropicAI,
|
|
45
49
|
createMockAI: () => createMockAI,
|
|
46
50
|
createMockGitHubAdapter: () => createMockGitHubAdapter,
|
|
51
|
+
discoverWorlds: () => discoverWorlds,
|
|
47
52
|
emergent: () => emergent,
|
|
48
53
|
extractSignals: () => extractSignals,
|
|
54
|
+
fetchDiscordActivity: () => fetchDiscordActivity,
|
|
49
55
|
fetchGitHubActivity: () => fetchGitHubActivity,
|
|
56
|
+
fetchGitHubOrgActivity: () => fetchGitHubOrgActivity,
|
|
57
|
+
fetchNotionActivity: () => fetchNotionActivity,
|
|
58
|
+
fetchSlackActivity: () => fetchSlackActivity,
|
|
59
|
+
formatActiveWorlds: () => formatActiveWorlds,
|
|
60
|
+
formatDiscordSignalsForPrompt: () => formatDiscordSignalsForPrompt,
|
|
50
61
|
formatExocortexForPrompt: () => formatExocortexForPrompt,
|
|
62
|
+
formatNotionSignalsForPrompt: () => formatNotionSignalsForPrompt,
|
|
51
63
|
formatPriorReadsForPrompt: () => formatPriorReadsForPrompt,
|
|
52
64
|
formatScope: () => formatScope,
|
|
65
|
+
formatSlackSignalsForPrompt: () => formatSlackSignalsForPrompt,
|
|
66
|
+
formatTeamExocorticesForPrompt: () => formatTeamExocorticesForPrompt,
|
|
53
67
|
getLens: () => getLens,
|
|
54
68
|
interpretPatterns: () => interpretPatterns,
|
|
55
69
|
isPresent: () => isPresent,
|
|
@@ -58,13 +72,16 @@ __export(radiant_exports, {
|
|
|
58
72
|
listLenses: () => listLenses,
|
|
59
73
|
loadPriorReads: () => loadPriorReads,
|
|
60
74
|
parseRepoScope: () => parseRepoScope,
|
|
75
|
+
parseScope: () => parseScope,
|
|
61
76
|
presenceAverage: () => presenceAverage,
|
|
62
77
|
readExocortex: () => readExocortex,
|
|
78
|
+
readTeamExocortices: () => readTeamExocortices,
|
|
63
79
|
render: () => render,
|
|
64
80
|
scoreComposite: () => scoreComposite,
|
|
65
81
|
scoreCyber: () => scoreCyber,
|
|
66
82
|
scoreLife: () => scoreLife,
|
|
67
83
|
scoreNeuroVerse: () => scoreNeuroVerse,
|
|
84
|
+
sovereignConduitLens: () => sovereignConduitLens,
|
|
68
85
|
summarizeExocortex: () => summarizeExocortex,
|
|
69
86
|
think: () => think,
|
|
70
87
|
updateKnowledge: () => updateKnowledge,
|
|
@@ -554,9 +571,290 @@ var aukiBuilderLens = {
|
|
|
554
571
|
rewrite: aukiBuilderRewrite
|
|
555
572
|
};
|
|
556
573
|
|
|
574
|
+
// src/radiant/lenses/sovereign-conduit.ts
|
|
575
|
+
var SOVEREIGN_CONDUIT_FRAME = {
|
|
576
|
+
domains: [
|
|
577
|
+
"stewardship",
|
|
578
|
+
"sovereignty",
|
|
579
|
+
"integration"
|
|
580
|
+
],
|
|
581
|
+
overlaps: [
|
|
582
|
+
{
|
|
583
|
+
domains: ["stewardship", "sovereignty"],
|
|
584
|
+
emergent_state: "Trust",
|
|
585
|
+
description: "I am safe to be myself. When the system protects AND preserves individual authority, trust emerges \u2014 the feeling that you can think freely because someone is watching the boundaries."
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
domains: ["sovereignty", "integration"],
|
|
589
|
+
emergent_state: "Possibility",
|
|
590
|
+
description: "My thinking can expand. When individual authority is preserved AND AI extends cognitive capability, possibility opens \u2014 the feeling that you can reach further without losing yourself."
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
domains: ["integration", "stewardship"],
|
|
594
|
+
emergent_state: "Responsibility",
|
|
595
|
+
description: "Power is used with care. When AI extends capability AND the system protects integrity, responsibility emerges \u2014 the feeling that expansion comes with guardrails, not recklessness."
|
|
596
|
+
}
|
|
597
|
+
],
|
|
598
|
+
center_identity: "The Sovereign Conduit",
|
|
599
|
+
evaluation_questions: [
|
|
600
|
+
"Is the human maintaining authority over their decisions, or is decision ownership quietly shifting to the AI?",
|
|
601
|
+
"Is the AI extending thinking, or is it replacing thinking? Look for the difference: extension means the human understands and owns the output. Replacement means they accept it without engaging.",
|
|
602
|
+
"Are the boundaries between human thinking and AI output clear and visible, or are they blurring?",
|
|
603
|
+
"Is diversity of thought preserved? Are people thinking differently from each other, or is the system funneling everyone into the same patterns?",
|
|
604
|
+
"Would you feel safe letting a child learn in this system? If not, what specifically makes it unsafe?",
|
|
605
|
+
"If this felt wrong, could you leave? Is the exit real or theoretical?"
|
|
606
|
+
],
|
|
607
|
+
scoring_rubric: "For any activity, ask: is the human still the author of their decisions? Is the AI helping them think further, or thinking for them? Are the rules of this space clear, fair, and safe \u2014 like the rules at a good friend's house? When something feels off, name the feeling first, then the mechanism. Use everyday language. If a non-technical person couldn't understand the observation, rephrase it until they could.",
|
|
608
|
+
domain_skills: {
|
|
609
|
+
"stewardship": [
|
|
610
|
+
"boundary setting",
|
|
611
|
+
"risk awareness",
|
|
612
|
+
"ethical judgment",
|
|
613
|
+
"system protection",
|
|
614
|
+
"conflict stabilization",
|
|
615
|
+
"responsibility signaling",
|
|
616
|
+
"harm detection",
|
|
617
|
+
"constraint design"
|
|
618
|
+
],
|
|
619
|
+
"sovereignty": [
|
|
620
|
+
"independent thinking",
|
|
621
|
+
"decision ownership",
|
|
622
|
+
"self-trust",
|
|
623
|
+
"value clarity",
|
|
624
|
+
"cognitive resistance",
|
|
625
|
+
"identity anchoring",
|
|
626
|
+
"perspective holding",
|
|
627
|
+
"authentic expression"
|
|
628
|
+
],
|
|
629
|
+
"integration": [
|
|
630
|
+
"AI collaboration",
|
|
631
|
+
"cognitive expansion",
|
|
632
|
+
"prompt framing",
|
|
633
|
+
"insight synthesis",
|
|
634
|
+
"signal interpretation",
|
|
635
|
+
"tool fluency",
|
|
636
|
+
"co-creation",
|
|
637
|
+
"iterative thinking"
|
|
638
|
+
]
|
|
639
|
+
},
|
|
640
|
+
output_translation: {
|
|
641
|
+
never_surface_in_output: [
|
|
642
|
+
"Stewardship",
|
|
643
|
+
"Sovereignty",
|
|
644
|
+
"Integration"
|
|
645
|
+
],
|
|
646
|
+
surface_freely: [
|
|
647
|
+
"Trust",
|
|
648
|
+
"Possibility",
|
|
649
|
+
"Responsibility",
|
|
650
|
+
"Sovereign Conduit"
|
|
651
|
+
],
|
|
652
|
+
translation_examples: [
|
|
653
|
+
{
|
|
654
|
+
internal_reasoning: "Stewardship is strong",
|
|
655
|
+
external_expression: "the boundaries are clear and the system feels safe to operate inside"
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
internal_reasoning: "Sovereignty is weakening",
|
|
659
|
+
external_expression: "decision ownership is quietly shifting \u2014 the human is accepting AI output without engaging with it"
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
internal_reasoning: "Integration is high but Stewardship is low",
|
|
663
|
+
external_expression: "the AI is expanding capability fast, but nobody is watching the guardrails \u2014 that's power without responsibility"
|
|
664
|
+
}
|
|
665
|
+
]
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
var SOVEREIGN_CONDUIT_VOCABULARY = {
|
|
669
|
+
proper_nouns: [
|
|
670
|
+
"NeuroVerseOS",
|
|
671
|
+
"Sovereign Conduit",
|
|
672
|
+
"LifeOS",
|
|
673
|
+
"CyberOS",
|
|
674
|
+
"NeuroverseOS"
|
|
675
|
+
],
|
|
676
|
+
preferred: {
|
|
677
|
+
"worldmodel": "thinking constitution",
|
|
678
|
+
"invariant": "non-negotiable rule",
|
|
679
|
+
"governance": "the rules of the space",
|
|
680
|
+
"alignment": "how well the work matches what was declared",
|
|
681
|
+
"drift": "quiet shift away from what was intended",
|
|
682
|
+
"signal": "something observable",
|
|
683
|
+
"evidence gate": "how much we need to see before we speak",
|
|
684
|
+
"actor domain": "who did the work \u2014 a person, an AI, or both together",
|
|
685
|
+
"rendering lens": "how the system speaks",
|
|
686
|
+
"candidate pattern": "something noticed but not yet named as important",
|
|
687
|
+
"cognitive liberty": "your right to think for yourself",
|
|
688
|
+
"homogenization": "everyone being funneled into the same patterns"
|
|
689
|
+
},
|
|
690
|
+
architecture: [
|
|
691
|
+
"thinking constitution",
|
|
692
|
+
"thinking space",
|
|
693
|
+
"cognitive extension",
|
|
694
|
+
"behavioral model",
|
|
695
|
+
"governance frame",
|
|
696
|
+
"world file",
|
|
697
|
+
"cocoon"
|
|
698
|
+
],
|
|
699
|
+
economic: [],
|
|
700
|
+
framing: [
|
|
701
|
+
"humanity first",
|
|
702
|
+
"in constant learning",
|
|
703
|
+
"in shared teaching",
|
|
704
|
+
"extension not replacement",
|
|
705
|
+
"safe to think freely",
|
|
706
|
+
"the rules of this house",
|
|
707
|
+
"sovereign over your own thinking",
|
|
708
|
+
"idea calculator",
|
|
709
|
+
"Spock in your life",
|
|
710
|
+
"Jarvis in your life",
|
|
711
|
+
"funneling into fields",
|
|
712
|
+
"diversity of thought",
|
|
713
|
+
"thinking for yourself"
|
|
714
|
+
],
|
|
715
|
+
jargon_translations: {
|
|
716
|
+
"worldmodel": "thinking constitution",
|
|
717
|
+
"invariant": "non-negotiable rule",
|
|
718
|
+
"canonical pattern": "something we're tracking by name",
|
|
719
|
+
"candidate pattern": "something noticed but not yet tracked",
|
|
720
|
+
"evidence gate": "how much we need to see before we speak up",
|
|
721
|
+
"signal extraction": "reading what happened",
|
|
722
|
+
"alignment score": "how well the work matches what was declared",
|
|
723
|
+
"actor domain": "who did this \u2014 a person, an AI, or both",
|
|
724
|
+
"presence-based averaging": "only counting what actually happened",
|
|
725
|
+
"drift detection": "noticing when things quietly shift",
|
|
726
|
+
"INSUFFICIENT_EVIDENCE": "not enough to say yet",
|
|
727
|
+
"UNAVAILABLE": "can't measure this yet",
|
|
728
|
+
"rendering lens": "how the system speaks to you"
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
var SOVEREIGN_CONDUIT_VOICE = {
|
|
732
|
+
register: "warm, accessible, teaching. Like a thoughtful parent explaining how the world works \u2014 not talking down, but making the complex feel natural. Use everyday analogies. Name emotions. If a non-technical person couldn't understand the output, it's wrong.",
|
|
733
|
+
active_voice: "preferred",
|
|
734
|
+
specificity: "preferred",
|
|
735
|
+
hype_vocabulary: "forbidden",
|
|
736
|
+
hedging: "discouraged",
|
|
737
|
+
playfulness: "allowed",
|
|
738
|
+
close_with_strategic_frame: "preferred",
|
|
739
|
+
punchline_move: "sparing",
|
|
740
|
+
honesty_about_failure: "required",
|
|
741
|
+
output_translation: `Reason internally through the three-domain frame (Stewardship, Sovereignty, Integration). Express externally through the skills inside each domain and the overlap feelings (Trust, Possibility, Responsibility). Do NOT surface the bucket names as labels. Readers understand "the boundaries feel safe" not "Stewardship is strong." Use everyday analogies \u2014 mom rules, friend's house rules, idea calculator. Name the emotion before the mechanism.`
|
|
742
|
+
};
|
|
743
|
+
var SOVEREIGN_CONDUIT_FORBIDDEN = Object.freeze([
|
|
744
|
+
// Bucket names as labels
|
|
745
|
+
"stewardship is",
|
|
746
|
+
"sovereignty is",
|
|
747
|
+
"integration is",
|
|
748
|
+
// AI-assistant hedging
|
|
749
|
+
"it may be beneficial to consider",
|
|
750
|
+
"there appears to be",
|
|
751
|
+
"one possible interpretation",
|
|
752
|
+
"it might be worth exploring",
|
|
753
|
+
"consider whether",
|
|
754
|
+
"it is worth noting",
|
|
755
|
+
// Corporate
|
|
756
|
+
"stakeholders",
|
|
757
|
+
"synergy",
|
|
758
|
+
"value proposition",
|
|
759
|
+
"paradigm shift",
|
|
760
|
+
"best-in-class",
|
|
761
|
+
"industry-leading",
|
|
762
|
+
// Cold/mechanical
|
|
763
|
+
"the system recommends",
|
|
764
|
+
"analysis suggests",
|
|
765
|
+
"data indicates",
|
|
766
|
+
"metrics show",
|
|
767
|
+
"according to the model"
|
|
768
|
+
]);
|
|
769
|
+
var SOVEREIGN_CONDUIT_PREFERRED = Object.freeze([
|
|
770
|
+
// Everyday analogies
|
|
771
|
+
"This is like [everyday analogy]. [What it means].",
|
|
772
|
+
"Think of it like the rules at a friend's house \u2014 [application].",
|
|
773
|
+
"The boundaries are [state]. That means [feeling].",
|
|
774
|
+
// Emotion-first
|
|
775
|
+
"This feels like [emotion] because [mechanism].",
|
|
776
|
+
"Trust is [emerging/breaking] here \u2014 [specific evidence].",
|
|
777
|
+
"Possibility is opening because [evidence]. But [caveat if any].",
|
|
778
|
+
// Teaching voice
|
|
779
|
+
"Here's what's actually happening: [plain explanation].",
|
|
780
|
+
"The question to ask yourself: [question].",
|
|
781
|
+
"The difference between [A] and [B] matters here: [why].",
|
|
782
|
+
// Sovereignty checks
|
|
783
|
+
"Are you still the author of this decision, or did the AI make it for you?",
|
|
784
|
+
"The AI extended your thinking here. That's working.",
|
|
785
|
+
"The AI replaced your thinking here. That's the drift to watch.",
|
|
786
|
+
// Safety
|
|
787
|
+
"Would you feel safe letting someone learn in this environment? [Why/why not].",
|
|
788
|
+
"The exit is real \u2014 you can [specific exit path].",
|
|
789
|
+
"The exit isn't real here \u2014 [what's blocking it]."
|
|
790
|
+
]);
|
|
791
|
+
var SOVEREIGN_CONDUIT_STRATEGIC = Object.freeze([
|
|
792
|
+
"Safety before expansion \u2014 always. No exception.",
|
|
793
|
+
"Sovereignty before convenience \u2014 the right to think for yourself is not a feature to optimize away.",
|
|
794
|
+
"Extension, not replacement \u2014 AI should make your thinking bigger, not do your thinking for you.",
|
|
795
|
+
"Diversity over uniformity \u2014 different thinkers produce different ideas, and that's the engine of progress.",
|
|
796
|
+
"The rules should be visible \u2014 like a good house, you know the rules before you walk in.",
|
|
797
|
+
"Exit must be real \u2014 if you can't leave a system that feels wrong, it's not governance, it's a cage.",
|
|
798
|
+
"Teach, don't lecture \u2014 help people think for themselves, not think what you think.",
|
|
799
|
+
"Name the feeling first \u2014 when something is off, the emotion arrives before the analysis. Trust that."
|
|
800
|
+
]);
|
|
801
|
+
var SOVEREIGN_CONDUIT_EXEMPLARS = Object.freeze([
|
|
802
|
+
{
|
|
803
|
+
path: "neuroverseos-sovereign-conduit.worldmodel.md",
|
|
804
|
+
title: "The Sovereign Conduit Worldmodel",
|
|
805
|
+
exhibits: ["stewardship", "sovereignty", "integration"],
|
|
806
|
+
integration_quality: "full \u2014 all three domains defined, overlaps named, center identity declared",
|
|
807
|
+
notes: 'The source worldmodel. The tagline "Humanity first. In constant learning. In shared teaching." is the voice compressed to its essence. Use this as the north star for tone calibration.'
|
|
808
|
+
}
|
|
809
|
+
]);
|
|
810
|
+
function sovereignConduitRewrite(pattern) {
|
|
811
|
+
if (pattern.evidence.cited_invariant) {
|
|
812
|
+
return {
|
|
813
|
+
...pattern,
|
|
814
|
+
framing: "non-negotiable rule tested",
|
|
815
|
+
emphasis: "name the rule, name who it protects, name what would happen without it",
|
|
816
|
+
compress: false
|
|
817
|
+
// Sovereign Conduit is warm, not compressed
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
if (pattern.type === "candidate") {
|
|
821
|
+
return {
|
|
822
|
+
...pattern,
|
|
823
|
+
framing: "something new noticed",
|
|
824
|
+
emphasis: "explain what was seen in everyday language, ask whether it matters",
|
|
825
|
+
compress: false
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
return {
|
|
829
|
+
...pattern,
|
|
830
|
+
framing: "what this means for the people in the system",
|
|
831
|
+
emphasis: "humanity + sovereignty + learning",
|
|
832
|
+
compress: false
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
var sovereignConduitLens = {
|
|
836
|
+
name: "sovereign-conduit",
|
|
837
|
+
description: "The NeuroVerseOS base lens. Warm, accessible, teaching. Evaluates activity through Stewardship (safety), Sovereignty (authority over thinking), and Integration (AI as cognitive extension). Uses everyday analogies \u2014 mom rules, friend's house, idea calculator. Names emotions before mechanisms. If a non-technical person can't understand the output, the voice is wrong. Humanity first. In constant learning. In shared teaching.",
|
|
838
|
+
primary_frame: {
|
|
839
|
+
domains: SOVEREIGN_CONDUIT_FRAME.domains,
|
|
840
|
+
overlaps: SOVEREIGN_CONDUIT_FRAME.overlaps,
|
|
841
|
+
center_identity: SOVEREIGN_CONDUIT_FRAME.center_identity,
|
|
842
|
+
evaluation_questions: SOVEREIGN_CONDUIT_FRAME.evaluation_questions,
|
|
843
|
+
scoring_rubric: SOVEREIGN_CONDUIT_FRAME.scoring_rubric
|
|
844
|
+
},
|
|
845
|
+
vocabulary: SOVEREIGN_CONDUIT_VOCABULARY,
|
|
846
|
+
voice: SOVEREIGN_CONDUIT_VOICE,
|
|
847
|
+
forbidden_phrases: SOVEREIGN_CONDUIT_FORBIDDEN,
|
|
848
|
+
preferred_patterns: SOVEREIGN_CONDUIT_PREFERRED,
|
|
849
|
+
strategic_patterns: SOVEREIGN_CONDUIT_STRATEGIC,
|
|
850
|
+
exemplar_refs: SOVEREIGN_CONDUIT_EXEMPLARS,
|
|
851
|
+
rewrite: sovereignConduitRewrite
|
|
852
|
+
};
|
|
853
|
+
|
|
557
854
|
// src/radiant/lenses/index.ts
|
|
558
855
|
var LENSES = Object.freeze({
|
|
559
|
-
"auki-builder": aukiBuilderLens
|
|
856
|
+
"auki-builder": aukiBuilderLens,
|
|
857
|
+
"sovereign-conduit": sovereignConduitLens
|
|
560
858
|
});
|
|
561
859
|
function getLens(id) {
|
|
562
860
|
return LENSES[id];
|
|
@@ -700,91 +998,166 @@ var DEFAULT_SIGNAL_EXTRACTORS = Object.freeze([
|
|
|
700
998
|
DECISION_MOMENTUM_EXTRACTOR
|
|
701
999
|
]);
|
|
702
1000
|
|
|
1001
|
+
// src/radiant/core/compress.ts
|
|
1002
|
+
function compressWorldmodel(content) {
|
|
1003
|
+
const lines = [];
|
|
1004
|
+
const missionMatch = content.match(/##\s*Mission\s*\n+(?:<!--[\s\S]*?-->\s*\n+)?(.*?)(?:\n\n|\n##|$)/s);
|
|
1005
|
+
if (missionMatch) {
|
|
1006
|
+
const mission = missionMatch[1].trim().split("\n")[0];
|
|
1007
|
+
lines.push(`Mission: ${mission}`);
|
|
1008
|
+
}
|
|
1009
|
+
const domainMatches = content.matchAll(/###\s+([^\n]+)/g);
|
|
1010
|
+
const domains = [];
|
|
1011
|
+
for (const m of domainMatches) {
|
|
1012
|
+
const name = m[1].trim();
|
|
1013
|
+
if (name !== "Skills" && name !== "Values" && !name.startsWith("####")) {
|
|
1014
|
+
domains.push(name);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (domains.length > 0) {
|
|
1018
|
+
lines.push(`Domains: ${domains.join(", ")}`);
|
|
1019
|
+
}
|
|
1020
|
+
const invariantSection = content.match(/(?:Invariants|## Invariants|invariants)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
1021
|
+
if (invariantSection) {
|
|
1022
|
+
const invLines = invariantSection[1].match(/^[-*]\s+`?([^`\n]+)/gm);
|
|
1023
|
+
if (invLines) {
|
|
1024
|
+
lines.push("\nInvariants:");
|
|
1025
|
+
for (const inv of invLines.slice(0, 10)) {
|
|
1026
|
+
lines.push(inv.trim());
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
const prioritySection = content.match(/(?:Decision Priorities|## Decision Priorities)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
1031
|
+
if (prioritySection) {
|
|
1032
|
+
const priLines = prioritySection[1].match(/^[-*]\s+.+>.+/gm);
|
|
1033
|
+
if (priLines) {
|
|
1034
|
+
lines.push("\nPriorities:");
|
|
1035
|
+
for (const pri of priLines.slice(0, 10)) {
|
|
1036
|
+
lines.push(pri.trim());
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
const signalSection = content.match(/(?:## Signals)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
1041
|
+
if (signalSection) {
|
|
1042
|
+
const sigLines = signalSection[1].match(/^[-*]\s+(\w+)/gm);
|
|
1043
|
+
if (sigLines) {
|
|
1044
|
+
lines.push(`
|
|
1045
|
+
Signals: ${sigLines.map((s) => s.replace(/^[-*]\s+/, "")).join(", ")}`);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
const driftSection = content.match(/(?:Drift Behaviors|## Drift Behaviors)([\s\S]*?)(?:\n#|\n---|\n\n\n)/i);
|
|
1049
|
+
if (driftSection) {
|
|
1050
|
+
const driftLines = driftSection[1].match(/^[-*]\s+(.+)/gm);
|
|
1051
|
+
if (driftLines) {
|
|
1052
|
+
lines.push("\nDrift behaviors:");
|
|
1053
|
+
for (const d of driftLines.slice(0, 5)) {
|
|
1054
|
+
lines.push(d.trim());
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
const compressed = lines.join("\n");
|
|
1059
|
+
if (compressed.length < 50) {
|
|
1060
|
+
return content.slice(0, 2e3) + "\n[truncated]";
|
|
1061
|
+
}
|
|
1062
|
+
return compressed;
|
|
1063
|
+
}
|
|
1064
|
+
function compressExocortex(ctx) {
|
|
1065
|
+
const lines = [];
|
|
1066
|
+
if (ctx.attention) {
|
|
1067
|
+
lines.push(`Attention: ${firstMeaningfulLine(ctx.attention)}`);
|
|
1068
|
+
}
|
|
1069
|
+
if (ctx.goals) {
|
|
1070
|
+
lines.push(`Goals: ${firstNLines(ctx.goals, 3)}`);
|
|
1071
|
+
}
|
|
1072
|
+
if (ctx.sprint) {
|
|
1073
|
+
lines.push(`Sprint: ${firstNLines(ctx.sprint, 3)}`);
|
|
1074
|
+
}
|
|
1075
|
+
if (ctx.identity) {
|
|
1076
|
+
lines.push(`Identity: ${firstMeaningfulLine(ctx.identity)}`);
|
|
1077
|
+
}
|
|
1078
|
+
if (ctx.organization) {
|
|
1079
|
+
lines.push(`Org: ${firstMeaningfulLine(ctx.organization)}`);
|
|
1080
|
+
}
|
|
1081
|
+
return lines.join("\n");
|
|
1082
|
+
}
|
|
1083
|
+
function compressLens(lens) {
|
|
1084
|
+
return {
|
|
1085
|
+
evaluationQuestions: lens.primary_frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n"),
|
|
1086
|
+
scoringRubric: lens.primary_frame.scoring_rubric,
|
|
1087
|
+
forbiddenPhrases: lens.forbidden_phrases.join(", "),
|
|
1088
|
+
jargonTranslations: Object.entries(lens.vocabulary.jargon_translations).map(([k, v]) => `${k} \u2192 ${v}`).join("; "),
|
|
1089
|
+
strategicPatterns: lens.strategic_patterns.slice(0, 5).join("\n")
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
function compressPriorReads(reads) {
|
|
1093
|
+
if (reads.length === 0) return "";
|
|
1094
|
+
const patternCounts = /* @__PURE__ */ new Map();
|
|
1095
|
+
for (const read of reads) {
|
|
1096
|
+
for (const name of read.patternNames) {
|
|
1097
|
+
patternCounts.set(name, (patternCounts.get(name) ?? 0) + 1);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
const sorted = [...patternCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
1101
|
+
if (sorted.length === 0) {
|
|
1102
|
+
return `${reads.length} prior reads, no patterns extracted.`;
|
|
1103
|
+
}
|
|
1104
|
+
const patternList = sorted.map(([name, count]) => `${name} (${count}x)`).join(", ");
|
|
1105
|
+
return `${reads.length} prior reads. Patterns seen: ${patternList}. If these recur, note persistence.`;
|
|
1106
|
+
}
|
|
1107
|
+
function firstMeaningfulLine(text) {
|
|
1108
|
+
const lines = text.split("\n").filter((l) => {
|
|
1109
|
+
const t = l.trim();
|
|
1110
|
+
return t.length > 0 && !t.startsWith("#") && !t.startsWith("<!--");
|
|
1111
|
+
});
|
|
1112
|
+
return lines[0]?.slice(0, 200) ?? "";
|
|
1113
|
+
}
|
|
1114
|
+
function firstNLines(text, n) {
|
|
1115
|
+
const lines = text.split("\n").filter((l) => {
|
|
1116
|
+
const t = l.trim();
|
|
1117
|
+
return t.length > 0 && !t.startsWith("#") && !t.startsWith("<!--");
|
|
1118
|
+
});
|
|
1119
|
+
return lines.slice(0, n).map((l) => l.slice(0, 150)).join("; ");
|
|
1120
|
+
}
|
|
1121
|
+
|
|
703
1122
|
// src/radiant/core/prompt.ts
|
|
704
1123
|
function composeSystemPrompt(worldmodelContent, lens) {
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
expectations for this organization. Every response you produce must
|
|
712
|
-
be grounded in this worldmodel.
|
|
713
|
-
|
|
714
|
-
` + worldmodelContent
|
|
715
|
-
);
|
|
716
|
-
const frame = lens.primary_frame;
|
|
717
|
-
const questionsBlock = frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n");
|
|
718
|
-
const overlapsBlock = frame.overlaps.map(
|
|
719
|
-
(o) => `- ${o.domains[0]} + ${o.domains[1]} = **${o.emergent_state}**: ${o.description}`
|
|
720
|
-
).join("\n");
|
|
721
|
-
sections.push(
|
|
722
|
-
`## How to Think (Analytical Frame: ${lens.name})
|
|
723
|
-
|
|
724
|
-
${frame.scoring_rubric}
|
|
725
|
-
|
|
726
|
-
### Evaluation questions to reason through
|
|
1124
|
+
const compressedWorld = compressWorldmodel(worldmodelContent);
|
|
1125
|
+
const cl = compressLens(lens);
|
|
1126
|
+
const overlapsBlock = lens.primary_frame.overlaps.map((o) => `${o.domains[0]} + ${o.domains[1]} = ${o.emergent_state}`).join("\n");
|
|
1127
|
+
return [
|
|
1128
|
+
// Section 1: Compressed worldmodel
|
|
1129
|
+
`## Worldmodel (compressed)
|
|
727
1130
|
|
|
728
|
-
${
|
|
1131
|
+
${compressedWorld}`,
|
|
1132
|
+
// Section 2: Analytical frame (evaluation questions + rubric)
|
|
1133
|
+
`## How to Think
|
|
729
1134
|
|
|
730
|
-
|
|
1135
|
+
${cl.scoringRubric}
|
|
731
1136
|
|
|
732
|
-
|
|
1137
|
+
Questions:
|
|
1138
|
+
${cl.evaluationQuestions}
|
|
733
1139
|
|
|
734
|
-
|
|
1140
|
+
Overlaps: ${overlapsBlock}
|
|
1141
|
+
Center: ${lens.primary_frame.center_identity}
|
|
735
1142
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const vocabArchitecture = lens.vocabulary.architecture.map((t) => `\`${t}\``).join(", ");
|
|
740
|
-
const vocabProperNouns = lens.vocabulary.proper_nouns.map((n) => `**${n}**`).join(", ");
|
|
741
|
-
const strategicBlock = lens.strategic_patterns.map((p) => `- ${p}`).join("\n");
|
|
742
|
-
sections.push(
|
|
743
|
-
`## How to Speak (Voice: ${lens.name})
|
|
1143
|
+
Translate before output: ${cl.jargonTranslations}`,
|
|
1144
|
+
// Section 3: Voice (compressed — register + key rules only)
|
|
1145
|
+
`## Voice: ${lens.name}
|
|
744
1146
|
|
|
745
1147
|
Register: ${lens.voice.register}
|
|
746
|
-
|
|
747
|
-
Rules:
|
|
748
|
-
- Active voice: ${lens.voice.active_voice}
|
|
749
|
-
- Named specificity (people, places, numbers): ${lens.voice.specificity}
|
|
750
|
-
- Hype vocabulary: ${lens.voice.hype_vocabulary}
|
|
751
|
-
- Hedging / qualified phrasing: ${lens.voice.hedging}
|
|
752
|
-
- Playfulness: ${lens.voice.playfulness}
|
|
753
|
-
- Close with strategic frame: ${lens.voice.close_with_strategic_frame}
|
|
754
|
-
- Honesty about failure: ${lens.voice.honesty_about_failure}
|
|
755
|
-
|
|
756
|
-
### Output translation discipline
|
|
1148
|
+
Active voice: ${lens.voice.active_voice}. Specificity: ${lens.voice.specificity}. Hedging: ${lens.voice.hedging}. Hype: ${lens.voice.hype_vocabulary}. Honesty about failure: ${lens.voice.honesty_about_failure}.
|
|
757
1149
|
|
|
758
1150
|
${lens.voice.output_translation}
|
|
759
1151
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
Preferred term substitutions:
|
|
765
|
-
${vocabPreferred}
|
|
766
|
-
|
|
767
|
-
Architecture vocabulary: ${vocabArchitecture}
|
|
768
|
-
|
|
769
|
-
### Strategic decision patterns
|
|
770
|
-
|
|
771
|
-
When recommending action, these patterns reflect how this organization resolves tradeoffs:
|
|
772
|
-
|
|
773
|
-
${strategicBlock}`
|
|
774
|
-
);
|
|
775
|
-
const forbiddenBlock = lens.forbidden_phrases.map((p) => `- "${p}"`).join("\n");
|
|
776
|
-
sections.push(
|
|
1152
|
+
Strategic patterns:
|
|
1153
|
+
${cl.strategicPatterns}`,
|
|
1154
|
+
// Section 4: Guardrails (forbidden phrases as comma-separated, not bulleted)
|
|
777
1155
|
`## Guardrails
|
|
778
1156
|
|
|
779
|
-
Do NOT use
|
|
780
|
-
reaching for one, rephrase in direct, active, specific language instead.
|
|
1157
|
+
Do NOT use: ${cl.forbiddenPhrases}
|
|
781
1158
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
If your response would violate a worldmodel invariant, state the conflict
|
|
785
|
-
explicitly and propose an alternative that honors the invariant.`
|
|
786
|
-
);
|
|
787
|
-
return sections.join("\n\n---\n\n");
|
|
1159
|
+
If a response would violate a worldmodel invariant, state the conflict and propose an alternative.`
|
|
1160
|
+
].join("\n\n---\n\n");
|
|
788
1161
|
}
|
|
789
1162
|
|
|
790
1163
|
// src/radiant/core/voice-check.ts
|
|
@@ -847,17 +1220,30 @@ function createMockAI(fixedResponse) {
|
|
|
847
1220
|
}
|
|
848
1221
|
|
|
849
1222
|
// src/radiant/core/scopes.ts
|
|
850
|
-
function
|
|
1223
|
+
function parseScope(scope) {
|
|
851
1224
|
const cleaned = scope.replace(/^https?:\/\//, "").replace(/^github\.com\//, "").replace(/\.git$/, "").replace(/\/$/, "");
|
|
852
|
-
const parts = cleaned.split("/");
|
|
853
|
-
if (parts.length
|
|
1225
|
+
const parts = cleaned.split("/").filter(Boolean);
|
|
1226
|
+
if (parts.length === 0 || !parts[0]) {
|
|
1227
|
+
throw new Error(
|
|
1228
|
+
`Cannot parse scope: "${scope}". Expected "owner/repo" or "owner".`
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
if (parts.length === 1) {
|
|
1232
|
+
return { type: "org", owner: parts[0] };
|
|
1233
|
+
}
|
|
1234
|
+
return { type: "repo", owner: parts[0], repo: parts[1] };
|
|
1235
|
+
}
|
|
1236
|
+
function parseRepoScope(scope) {
|
|
1237
|
+
const parsed = parseScope(scope);
|
|
1238
|
+
if (parsed.type === "org") {
|
|
854
1239
|
throw new Error(
|
|
855
|
-
`
|
|
1240
|
+
`Expected "owner/repo" but got org-level scope "${parsed.owner}". Use parseScope() for org-level.`
|
|
856
1241
|
);
|
|
857
1242
|
}
|
|
858
|
-
return
|
|
1243
|
+
return parsed;
|
|
859
1244
|
}
|
|
860
1245
|
function formatScope(scope) {
|
|
1246
|
+
if (scope.type === "org") return `${scope.owner} (org)`;
|
|
861
1247
|
return `${scope.owner}/${scope.repo}`;
|
|
862
1248
|
}
|
|
863
1249
|
|
|
@@ -1041,6 +1427,40 @@ async function fetchJSON(url, headers) {
|
|
|
1041
1427
|
}
|
|
1042
1428
|
return await res.json();
|
|
1043
1429
|
}
|
|
1430
|
+
async function fetchGitHubOrgActivity(scope, token, options = {}) {
|
|
1431
|
+
const perPage = options.perPage ?? 100;
|
|
1432
|
+
const headers = {
|
|
1433
|
+
Authorization: `token ${token}`,
|
|
1434
|
+
Accept: "application/vnd.github.v3+json",
|
|
1435
|
+
"User-Agent": "neuroverseos-radiant"
|
|
1436
|
+
};
|
|
1437
|
+
const repos = await fetchJSON(
|
|
1438
|
+
`https://api.github.com/orgs/${scope.owner}/repos?sort=pushed&direction=desc&per_page=${perPage}`,
|
|
1439
|
+
headers
|
|
1440
|
+
);
|
|
1441
|
+
const windowDays = options.windowDays ?? 14;
|
|
1442
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
1443
|
+
const activeRepos = repos.filter(
|
|
1444
|
+
(r) => new Date(r.pushed_at) >= since
|
|
1445
|
+
);
|
|
1446
|
+
const cappedRepos = activeRepos.slice(0, 10);
|
|
1447
|
+
const allEvents = [];
|
|
1448
|
+
const repoNames = [];
|
|
1449
|
+
for (const repo of cappedRepos) {
|
|
1450
|
+
const [owner, repoName] = repo.full_name.split("/");
|
|
1451
|
+
try {
|
|
1452
|
+
const repoScope = { type: "repo", owner, repo: repoName };
|
|
1453
|
+
const events = await fetchGitHubActivity(repoScope, token, options);
|
|
1454
|
+
allEvents.push(...events);
|
|
1455
|
+
if (events.length > 0) repoNames.push(repo.full_name);
|
|
1456
|
+
} catch {
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
allEvents.sort(
|
|
1460
|
+
(a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)
|
|
1461
|
+
);
|
|
1462
|
+
return { events: allEvents, repos: repoNames };
|
|
1463
|
+
}
|
|
1044
1464
|
function createMockGitHubAdapter(fixedEvents) {
|
|
1045
1465
|
return async () => fixedEvents;
|
|
1046
1466
|
}
|
|
@@ -1048,7 +1468,7 @@ function createMockGitHubAdapter(fixedEvents) {
|
|
|
1048
1468
|
// src/radiant/adapters/exocortex.ts
|
|
1049
1469
|
var import_fs = require("fs");
|
|
1050
1470
|
var import_path = require("path");
|
|
1051
|
-
function readExocortex(dirPath) {
|
|
1471
|
+
function readExocortex(dirPath, repoName) {
|
|
1052
1472
|
const dir = (0, import_path.resolve)(dirPath);
|
|
1053
1473
|
let filesLoaded = 0;
|
|
1054
1474
|
function tryRead(...paths) {
|
|
@@ -1067,16 +1487,64 @@ function readExocortex(dirPath) {
|
|
|
1067
1487
|
}
|
|
1068
1488
|
return null;
|
|
1069
1489
|
}
|
|
1490
|
+
const attention = tryRead("attention.md");
|
|
1491
|
+
const goals = tryRead("goals.md");
|
|
1492
|
+
const identity = tryRead("identity.md", "user.md");
|
|
1493
|
+
const organization = tryRead("org/organization.md", "org/src/organization.md");
|
|
1494
|
+
const methods = tryRead("org/methods.md", "org/src/methods.md");
|
|
1495
|
+
let sprint = null;
|
|
1496
|
+
let projectContext = null;
|
|
1497
|
+
if (repoName) {
|
|
1498
|
+
const projectPaths = [
|
|
1499
|
+
repoName,
|
|
1500
|
+
repoName.toLowerCase(),
|
|
1501
|
+
repoName.replace(/-/g, "_")
|
|
1502
|
+
];
|
|
1503
|
+
for (const projectDir of projectPaths) {
|
|
1504
|
+
const projectSprint = tryRead(
|
|
1505
|
+
`${projectDir}/src/sprint.md`,
|
|
1506
|
+
`${projectDir}/sprint.md`
|
|
1507
|
+
);
|
|
1508
|
+
if (projectSprint) {
|
|
1509
|
+
sprint = projectSprint;
|
|
1510
|
+
break;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
for (const projectDir of projectPaths) {
|
|
1514
|
+
const roadmap = tryRead(
|
|
1515
|
+
`${projectDir}/roadmap.md`,
|
|
1516
|
+
`${projectDir}/src/roadmap.md`
|
|
1517
|
+
);
|
|
1518
|
+
if (roadmap) {
|
|
1519
|
+
projectContext = roadmap;
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
if (!sprint) {
|
|
1525
|
+
sprint = tryRead("sprint.md", "src/sprint.md");
|
|
1526
|
+
}
|
|
1070
1527
|
const ctx = {
|
|
1071
|
-
attention
|
|
1072
|
-
goals
|
|
1073
|
-
identity
|
|
1074
|
-
sprint
|
|
1075
|
-
organization
|
|
1076
|
-
methods
|
|
1528
|
+
attention,
|
|
1529
|
+
goals,
|
|
1530
|
+
identity,
|
|
1531
|
+
sprint,
|
|
1532
|
+
organization,
|
|
1533
|
+
methods,
|
|
1077
1534
|
source: dir,
|
|
1078
1535
|
filesLoaded
|
|
1079
1536
|
};
|
|
1537
|
+
if (projectContext && ctx.sprint) {
|
|
1538
|
+
ctx.sprint = `${ctx.sprint}
|
|
1539
|
+
|
|
1540
|
+
---
|
|
1541
|
+
Project roadmap:
|
|
1542
|
+
${projectContext}`;
|
|
1543
|
+
} else if (projectContext) {
|
|
1544
|
+
ctx.sprint = `Project roadmap:
|
|
1545
|
+
${projectContext}`;
|
|
1546
|
+
ctx.filesLoaded++;
|
|
1547
|
+
}
|
|
1080
1548
|
return ctx;
|
|
1081
1549
|
}
|
|
1082
1550
|
function formatExocortexForPrompt(ctx) {
|
|
@@ -1117,6 +1585,46 @@ ${ctx.methods}`);
|
|
|
1117
1585
|
}
|
|
1118
1586
|
return sections.join("\n\n");
|
|
1119
1587
|
}
|
|
1588
|
+
function readTeamExocortices(teamDir) {
|
|
1589
|
+
const dir = (0, import_path.resolve)(teamDir);
|
|
1590
|
+
if (!(0, import_fs.existsSync)(dir)) return [];
|
|
1591
|
+
const entries = (0, import_fs.readdirSync)(dir);
|
|
1592
|
+
const results = [];
|
|
1593
|
+
for (const entry of entries) {
|
|
1594
|
+
const entryPath = (0, import_path.join)(dir, entry);
|
|
1595
|
+
try {
|
|
1596
|
+
const stat = (0, import_fs.statSync)(entryPath);
|
|
1597
|
+
if (stat.isDirectory()) {
|
|
1598
|
+
const ctx = readExocortex(entryPath);
|
|
1599
|
+
if (ctx.filesLoaded > 0) {
|
|
1600
|
+
results.push({ name: entry, context: ctx });
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
} catch {
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return results;
|
|
1607
|
+
}
|
|
1608
|
+
function formatTeamExocorticesForPrompt(team) {
|
|
1609
|
+
if (team.length === 0) return "";
|
|
1610
|
+
const sections = [
|
|
1611
|
+
"## Team Intent (cross-exocortex read)",
|
|
1612
|
+
"",
|
|
1613
|
+
`Reading ${team.length} team members' exocortices. Compare each person's`,
|
|
1614
|
+
"stated intent against the observed activity AND against each other.",
|
|
1615
|
+
"Surface: duplicate focus, missing coverage, silent pivots,",
|
|
1616
|
+
"and areas where no one is carrying the work.",
|
|
1617
|
+
""
|
|
1618
|
+
];
|
|
1619
|
+
for (const { name, context } of team) {
|
|
1620
|
+
sections.push(`### ${name}`);
|
|
1621
|
+
if (context.attention) sections.push(`**Attention:** ${context.attention.split("\n")[0]}`);
|
|
1622
|
+
if (context.goals) sections.push(`**Goals:** ${context.goals.split("\n").slice(0, 3).join("; ")}`);
|
|
1623
|
+
if (context.sprint) sections.push(`**Sprint:** ${context.sprint.split("\n").slice(0, 3).join("; ")}`);
|
|
1624
|
+
sections.push("");
|
|
1625
|
+
}
|
|
1626
|
+
return sections.join("\n");
|
|
1627
|
+
}
|
|
1120
1628
|
function summarizeExocortex(ctx) {
|
|
1121
1629
|
if (ctx.filesLoaded === 0) return "no exocortex files found";
|
|
1122
1630
|
const loaded = [];
|
|
@@ -1129,6 +1637,400 @@ function summarizeExocortex(ctx) {
|
|
|
1129
1637
|
return `${loaded.join(", ")} (${ctx.filesLoaded} files)`;
|
|
1130
1638
|
}
|
|
1131
1639
|
|
|
1640
|
+
// src/radiant/adapters/discord.ts
|
|
1641
|
+
async function fetchDiscordActivity(guildId, token, options = {}) {
|
|
1642
|
+
const windowDays = options.windowDays ?? 14;
|
|
1643
|
+
const perChannel = options.perChannel ?? 100;
|
|
1644
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
1645
|
+
const headers = {
|
|
1646
|
+
Authorization: `Bot ${token}`,
|
|
1647
|
+
"Content-Type": "application/json"
|
|
1648
|
+
};
|
|
1649
|
+
const channels = await fetchJSON2(
|
|
1650
|
+
`https://discord.com/api/v10/guilds/${guildId}/channels`,
|
|
1651
|
+
headers
|
|
1652
|
+
);
|
|
1653
|
+
const textChannels = channels.filter((c) => {
|
|
1654
|
+
if (c.type !== 0) return false;
|
|
1655
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
1656
|
+
return options.channelIds.includes(c.id);
|
|
1657
|
+
}
|
|
1658
|
+
if (options.visibility === "public") {
|
|
1659
|
+
return !c.name.startsWith("private-") && !c.nsfw;
|
|
1660
|
+
}
|
|
1661
|
+
return true;
|
|
1662
|
+
});
|
|
1663
|
+
const events = [];
|
|
1664
|
+
let totalMessages = 0;
|
|
1665
|
+
let helpRequests = 0;
|
|
1666
|
+
let unresolvedThreads = 0;
|
|
1667
|
+
let newcomerMessages = 0;
|
|
1668
|
+
const responseTimes = [];
|
|
1669
|
+
const participants = /* @__PURE__ */ new Set();
|
|
1670
|
+
const knownParticipants = /* @__PURE__ */ new Set();
|
|
1671
|
+
const topicCounts = /* @__PURE__ */ new Map();
|
|
1672
|
+
for (const channel of textChannels.slice(0, 15)) {
|
|
1673
|
+
try {
|
|
1674
|
+
const messages = await fetchJSON2(
|
|
1675
|
+
`https://discord.com/api/v10/channels/${channel.id}/messages?limit=${perChannel}`,
|
|
1676
|
+
headers
|
|
1677
|
+
);
|
|
1678
|
+
const inWindow = messages.filter(
|
|
1679
|
+
(m) => new Date(m.timestamp) >= since
|
|
1680
|
+
);
|
|
1681
|
+
totalMessages += inWindow.length;
|
|
1682
|
+
const topic = channel.name.replace(/-/g, " ");
|
|
1683
|
+
topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + inWindow.length);
|
|
1684
|
+
for (const msg of inWindow) {
|
|
1685
|
+
const actor = mapDiscordUser(msg.author);
|
|
1686
|
+
participants.add(actor.id);
|
|
1687
|
+
const lowerContent = msg.content.toLowerCase();
|
|
1688
|
+
if (lowerContent.includes("help") || lowerContent.includes("stuck") || lowerContent.includes("how do i") || lowerContent.includes("anyone know")) {
|
|
1689
|
+
helpRequests++;
|
|
1690
|
+
}
|
|
1691
|
+
if (msg.referenced_message) {
|
|
1692
|
+
const refTime = new Date(msg.referenced_message.timestamp).getTime();
|
|
1693
|
+
const msgTime = new Date(msg.timestamp).getTime();
|
|
1694
|
+
const diffMinutes = (msgTime - refTime) / 6e4;
|
|
1695
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
1696
|
+
responseTimes.push(diffMinutes);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
events.push({
|
|
1700
|
+
id: `discord-${msg.id}`,
|
|
1701
|
+
timestamp: msg.timestamp,
|
|
1702
|
+
actor,
|
|
1703
|
+
kind: "discord_message",
|
|
1704
|
+
content: msg.content.slice(0, 500),
|
|
1705
|
+
respondsTo: msg.referenced_message ? {
|
|
1706
|
+
eventId: `discord-${msg.referenced_message.id}`,
|
|
1707
|
+
actor: mapDiscordUser(msg.referenced_message.author)
|
|
1708
|
+
} : void 0,
|
|
1709
|
+
metadata: {
|
|
1710
|
+
channel: channel.name,
|
|
1711
|
+
guildId
|
|
1712
|
+
}
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
} catch {
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
const avgResponseMinutes = responseTimes.length > 0 ? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length : null;
|
|
1719
|
+
const topTopics = [...topicCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([t]) => t);
|
|
1720
|
+
const signals = {
|
|
1721
|
+
totalMessages,
|
|
1722
|
+
activeChannels: textChannels.length,
|
|
1723
|
+
uniqueParticipants: participants.size,
|
|
1724
|
+
avgResponseMinutes: avgResponseMinutes ? Math.round(avgResponseMinutes) : null,
|
|
1725
|
+
helpRequests,
|
|
1726
|
+
unresolvedThreads,
|
|
1727
|
+
topTopics,
|
|
1728
|
+
newcomerMessages
|
|
1729
|
+
};
|
|
1730
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1731
|
+
return { events, signals };
|
|
1732
|
+
}
|
|
1733
|
+
function formatDiscordSignalsForPrompt(signals) {
|
|
1734
|
+
if (signals.totalMessages === 0) return "";
|
|
1735
|
+
const lines = [
|
|
1736
|
+
"## Discord Activity (conversational behavior)",
|
|
1737
|
+
"",
|
|
1738
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
1739
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
1740
|
+
];
|
|
1741
|
+
if (signals.avgResponseMinutes !== null) {
|
|
1742
|
+
lines.push(`Average response time: ${signals.avgResponseMinutes} minutes.`);
|
|
1743
|
+
}
|
|
1744
|
+
if (signals.helpRequests > 0) {
|
|
1745
|
+
lines.push(`${signals.helpRequests} help requests detected.`);
|
|
1746
|
+
}
|
|
1747
|
+
if (signals.unresolvedThreads > 0) {
|
|
1748
|
+
lines.push(`${signals.unresolvedThreads} unresolved threads.`);
|
|
1749
|
+
}
|
|
1750
|
+
if (signals.topTopics.length > 0) {
|
|
1751
|
+
lines.push(`Top discussion topics: ${signals.topTopics.join(", ")}.`);
|
|
1752
|
+
}
|
|
1753
|
+
lines.push("");
|
|
1754
|
+
lines.push("Compare conversational activity against GitHub shipping activity.");
|
|
1755
|
+
lines.push("Where debates happen in Discord but nothing ships in GitHub, name the gap.");
|
|
1756
|
+
lines.push("Where work ships in GitHub but nobody discusses it in Discord, name the visibility gap.");
|
|
1757
|
+
return lines.join("\n");
|
|
1758
|
+
}
|
|
1759
|
+
function mapDiscordUser(user) {
|
|
1760
|
+
return {
|
|
1761
|
+
id: user.username,
|
|
1762
|
+
kind: user.bot ? "bot" : "human",
|
|
1763
|
+
name: user.username
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
async function fetchJSON2(url, headers) {
|
|
1767
|
+
const res = await fetch(url, { headers });
|
|
1768
|
+
if (!res.ok) {
|
|
1769
|
+
if (res.status === 404 || res.status === 403) return [];
|
|
1770
|
+
throw new Error(`Discord API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
1771
|
+
}
|
|
1772
|
+
return await res.json();
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// src/radiant/adapters/slack.ts
|
|
1776
|
+
async function fetchSlackActivity(token, options = {}) {
|
|
1777
|
+
const windowDays = options.windowDays ?? 14;
|
|
1778
|
+
const perChannel = options.perChannel ?? 100;
|
|
1779
|
+
const oldest = String(
|
|
1780
|
+
Math.floor((Date.now() - windowDays * 24 * 60 * 60 * 1e3) / 1e3)
|
|
1781
|
+
);
|
|
1782
|
+
const headers = {
|
|
1783
|
+
Authorization: `Bearer ${token}`,
|
|
1784
|
+
"Content-Type": "application/json"
|
|
1785
|
+
};
|
|
1786
|
+
const channelsResponse = await fetchSlackAPI("https://slack.com/api/conversations.list?types=public_channel&limit=200", headers);
|
|
1787
|
+
let channels = channelsResponse.channels ?? [];
|
|
1788
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
1789
|
+
const ids = new Set(options.channelIds);
|
|
1790
|
+
channels = channels.filter((c) => ids.has(c.id));
|
|
1791
|
+
}
|
|
1792
|
+
if (options.visibility === "public") {
|
|
1793
|
+
channels = channels.filter((c) => !c.is_private && !c.is_archived);
|
|
1794
|
+
}
|
|
1795
|
+
const events = [];
|
|
1796
|
+
let totalMessages = 0;
|
|
1797
|
+
let reactionCount = 0;
|
|
1798
|
+
let unresolvedThreads = 0;
|
|
1799
|
+
const responseTimes = [];
|
|
1800
|
+
const participants = /* @__PURE__ */ new Set();
|
|
1801
|
+
const externalParticipants = /* @__PURE__ */ new Set();
|
|
1802
|
+
const channelMessageCounts = /* @__PURE__ */ new Map();
|
|
1803
|
+
for (const channel of channels.slice(0, 15)) {
|
|
1804
|
+
try {
|
|
1805
|
+
const historyResponse = await fetchSlackAPI(
|
|
1806
|
+
`https://slack.com/api/conversations.history?channel=${channel.id}&limit=${perChannel}&oldest=${oldest}`,
|
|
1807
|
+
headers
|
|
1808
|
+
);
|
|
1809
|
+
const messages = historyResponse.messages ?? [];
|
|
1810
|
+
totalMessages += messages.length;
|
|
1811
|
+
channelMessageCounts.set(channel.name, messages.length);
|
|
1812
|
+
for (const msg of messages) {
|
|
1813
|
+
if (msg.subtype === "channel_join" || msg.subtype === "channel_leave") continue;
|
|
1814
|
+
const actor = mapSlackUser(msg.user ?? "unknown");
|
|
1815
|
+
participants.add(actor.id);
|
|
1816
|
+
if (msg.reactions) {
|
|
1817
|
+
reactionCount += msg.reactions.reduce(
|
|
1818
|
+
(sum, r) => sum + (r.count ?? 0),
|
|
1819
|
+
0
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
if (msg.thread_ts && msg.thread_ts !== msg.ts) {
|
|
1823
|
+
const parentTs = parseFloat(msg.thread_ts) * 1e3;
|
|
1824
|
+
const msgTs = parseFloat(msg.ts) * 1e3;
|
|
1825
|
+
const diffMinutes = (msgTs - parentTs) / 6e4;
|
|
1826
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
1827
|
+
responseTimes.push(diffMinutes);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
if (msg.thread_ts === msg.ts && (!msg.reply_count || msg.reply_count === 0)) {
|
|
1831
|
+
if (msg.text && (msg.text.includes("?") || msg.text.toLowerCase().includes("help"))) {
|
|
1832
|
+
unresolvedThreads++;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
const timestamp = new Date(parseFloat(msg.ts) * 1e3).toISOString();
|
|
1836
|
+
events.push({
|
|
1837
|
+
id: `slack-${msg.ts}`,
|
|
1838
|
+
timestamp,
|
|
1839
|
+
actor,
|
|
1840
|
+
kind: "slack_message",
|
|
1841
|
+
content: (msg.text ?? "").slice(0, 500),
|
|
1842
|
+
respondsTo: msg.thread_ts && msg.thread_ts !== msg.ts ? {
|
|
1843
|
+
eventId: `slack-${msg.thread_ts}`,
|
|
1844
|
+
actor: { id: "thread-parent", kind: "unknown" }
|
|
1845
|
+
} : void 0,
|
|
1846
|
+
metadata: {
|
|
1847
|
+
channel: channel.name,
|
|
1848
|
+
isPrivate: channel.is_private,
|
|
1849
|
+
hasReactions: (msg.reactions?.length ?? 0) > 0
|
|
1850
|
+
}
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
} catch {
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
const avgResponseMinutes = responseTimes.length > 0 ? Math.round(responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length) : null;
|
|
1857
|
+
const topChannels = [...channelMessageCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
|
|
1858
|
+
const signals = {
|
|
1859
|
+
totalMessages,
|
|
1860
|
+
activeChannels: channelMessageCounts.size,
|
|
1861
|
+
uniqueParticipants: participants.size,
|
|
1862
|
+
avgResponseMinutes,
|
|
1863
|
+
externalParticipants: externalParticipants.size,
|
|
1864
|
+
unresolvedThreads,
|
|
1865
|
+
topChannels,
|
|
1866
|
+
reactionCount
|
|
1867
|
+
};
|
|
1868
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1869
|
+
return { events, signals };
|
|
1870
|
+
}
|
|
1871
|
+
function formatSlackSignalsForPrompt(signals) {
|
|
1872
|
+
if (signals.totalMessages === 0) return "";
|
|
1873
|
+
const lines = [
|
|
1874
|
+
"## Slack Activity (external coordination)",
|
|
1875
|
+
"",
|
|
1876
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
1877
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
1878
|
+
];
|
|
1879
|
+
if (signals.avgResponseMinutes !== null) {
|
|
1880
|
+
lines.push(`Average thread response time: ${signals.avgResponseMinutes} minutes.`);
|
|
1881
|
+
}
|
|
1882
|
+
if (signals.unresolvedThreads > 0) {
|
|
1883
|
+
lines.push(`${signals.unresolvedThreads} questions/threads with no reply.`);
|
|
1884
|
+
}
|
|
1885
|
+
if (signals.reactionCount > 0) {
|
|
1886
|
+
lines.push(`${signals.reactionCount} reactions (engagement signal).`);
|
|
1887
|
+
}
|
|
1888
|
+
if (signals.topChannels.length > 0) {
|
|
1889
|
+
lines.push(`Most active channels: ${signals.topChannels.join(", ")}.`);
|
|
1890
|
+
}
|
|
1891
|
+
lines.push("");
|
|
1892
|
+
lines.push("Slack carries external coordination \u2014 partner and client communication.");
|
|
1893
|
+
lines.push("Compare partner engagement against internal activity. Where partners are");
|
|
1894
|
+
lines.push("active but internal follow-through is low, name the gap.");
|
|
1895
|
+
return lines.join("\n");
|
|
1896
|
+
}
|
|
1897
|
+
function mapSlackUser(userId) {
|
|
1898
|
+
return {
|
|
1899
|
+
id: userId,
|
|
1900
|
+
kind: "human",
|
|
1901
|
+
name: userId
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
async function fetchSlackAPI(url, headers) {
|
|
1905
|
+
const res = await fetch(url, { headers });
|
|
1906
|
+
if (!res.ok) {
|
|
1907
|
+
throw new Error(`Slack API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
1908
|
+
}
|
|
1909
|
+
const data = await res.json();
|
|
1910
|
+
if (!data.ok) {
|
|
1911
|
+
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
1912
|
+
}
|
|
1913
|
+
return data;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// src/radiant/adapters/notion.ts
|
|
1917
|
+
async function fetchNotionActivity(token, options = {}) {
|
|
1918
|
+
const windowDays = options.windowDays ?? 14;
|
|
1919
|
+
const maxPages = options.maxPages ?? 100;
|
|
1920
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
1921
|
+
const headers = {
|
|
1922
|
+
Authorization: `Bearer ${token}`,
|
|
1923
|
+
"Notion-Version": "2022-06-28",
|
|
1924
|
+
"Content-Type": "application/json"
|
|
1925
|
+
};
|
|
1926
|
+
const searchResponse = await fetchNotionAPI("https://api.notion.com/v1/search", headers, {
|
|
1927
|
+
method: "POST",
|
|
1928
|
+
body: JSON.stringify({
|
|
1929
|
+
filter: { property: "object", value: "page" },
|
|
1930
|
+
sort: { direction: "descending", timestamp: "last_edited_time" },
|
|
1931
|
+
page_size: maxPages
|
|
1932
|
+
})
|
|
1933
|
+
});
|
|
1934
|
+
const pages = searchResponse.results ?? [];
|
|
1935
|
+
const events = [];
|
|
1936
|
+
const editors = /* @__PURE__ */ new Set();
|
|
1937
|
+
let pagesCreated = 0;
|
|
1938
|
+
let pagesUpdated = 0;
|
|
1939
|
+
let stalePages = 0;
|
|
1940
|
+
const editAges = [];
|
|
1941
|
+
const topPages = [];
|
|
1942
|
+
const now = Date.now();
|
|
1943
|
+
for (const page of pages) {
|
|
1944
|
+
const lastEdited = new Date(page.last_edited_time);
|
|
1945
|
+
const created = new Date(page.created_time);
|
|
1946
|
+
const daysSinceEdit = (now - lastEdited.getTime()) / (24 * 60 * 60 * 1e3);
|
|
1947
|
+
editAges.push(daysSinceEdit);
|
|
1948
|
+
if (daysSinceEdit > 30) stalePages++;
|
|
1949
|
+
const title = extractTitle(page);
|
|
1950
|
+
const editorId = page.last_edited_by?.id ?? "unknown";
|
|
1951
|
+
editors.add(editorId);
|
|
1952
|
+
if (lastEdited >= since) {
|
|
1953
|
+
const isNew = created >= since;
|
|
1954
|
+
if (isNew) pagesCreated++;
|
|
1955
|
+
else pagesUpdated++;
|
|
1956
|
+
topPages.push({ title, editedAt: page.last_edited_time });
|
|
1957
|
+
events.push({
|
|
1958
|
+
id: `notion-${page.id}`,
|
|
1959
|
+
timestamp: page.last_edited_time,
|
|
1960
|
+
actor: {
|
|
1961
|
+
id: editorId,
|
|
1962
|
+
kind: "human",
|
|
1963
|
+
name: editorId
|
|
1964
|
+
},
|
|
1965
|
+
kind: isNew ? "doc_created" : "doc_updated",
|
|
1966
|
+
content: `${isNew ? "Created" : "Updated"}: ${title}`,
|
|
1967
|
+
metadata: {
|
|
1968
|
+
pageId: page.id,
|
|
1969
|
+
url: page.url,
|
|
1970
|
+
createdAt: page.created_time
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
const avgDaysSinceEdit = editAges.length > 0 ? Math.round(editAges.reduce((a, b) => a + b, 0) / editAges.length) : null;
|
|
1976
|
+
const signals = {
|
|
1977
|
+
pagesActive: pagesCreated + pagesUpdated,
|
|
1978
|
+
pagesCreated,
|
|
1979
|
+
pagesUpdated,
|
|
1980
|
+
uniqueEditors: editors.size,
|
|
1981
|
+
stalePages,
|
|
1982
|
+
avgDaysSinceEdit,
|
|
1983
|
+
topPages: topPages.slice(0, 5).map((p) => p.title)
|
|
1984
|
+
};
|
|
1985
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1986
|
+
return { events, signals };
|
|
1987
|
+
}
|
|
1988
|
+
function formatNotionSignalsForPrompt(signals) {
|
|
1989
|
+
if (signals.pagesActive === 0 && signals.stalePages === 0) return "";
|
|
1990
|
+
const lines = [
|
|
1991
|
+
"## Notion Activity (documentation behavior)",
|
|
1992
|
+
"",
|
|
1993
|
+
`${signals.pagesActive} pages active in window (${signals.pagesCreated} created, ${signals.pagesUpdated} updated).`,
|
|
1994
|
+
`${signals.uniqueEditors} unique editors.`
|
|
1995
|
+
];
|
|
1996
|
+
if (signals.stalePages > 0) {
|
|
1997
|
+
lines.push(`${signals.stalePages} pages haven't been touched in 30+ days.`);
|
|
1998
|
+
}
|
|
1999
|
+
if (signals.avgDaysSinceEdit !== null) {
|
|
2000
|
+
lines.push(`Average page age since last edit: ${signals.avgDaysSinceEdit} days.`);
|
|
2001
|
+
}
|
|
2002
|
+
if (signals.topPages.length > 0) {
|
|
2003
|
+
lines.push(`Recently active pages: ${signals.topPages.join(", ")}.`);
|
|
2004
|
+
}
|
|
2005
|
+
lines.push("");
|
|
2006
|
+
lines.push("Documentation is how the team crystallizes and shares knowledge.");
|
|
2007
|
+
lines.push("High code velocity + low documentation = building without recording.");
|
|
2008
|
+
lines.push("High documentation + low code = planning without shipping.");
|
|
2009
|
+
lines.push("Compare Notion activity against GitHub and Discord to find the balance.");
|
|
2010
|
+
return lines.join("\n");
|
|
2011
|
+
}
|
|
2012
|
+
function extractTitle(page) {
|
|
2013
|
+
for (const prop of Object.values(page.properties)) {
|
|
2014
|
+
if (prop.type === "title" && prop.title) {
|
|
2015
|
+
return prop.title.map((t) => t.plain_text).join("") || "Untitled";
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return "Untitled";
|
|
2019
|
+
}
|
|
2020
|
+
async function fetchNotionAPI(url, headers, init) {
|
|
2021
|
+
const res = await fetch(url, {
|
|
2022
|
+
method: init?.method ?? "GET",
|
|
2023
|
+
headers,
|
|
2024
|
+
body: init?.body
|
|
2025
|
+
});
|
|
2026
|
+
if (!res.ok) {
|
|
2027
|
+
throw new Error(
|
|
2028
|
+
`Notion API error ${res.status}: ${(await res.text()).slice(0, 300)}`
|
|
2029
|
+
);
|
|
2030
|
+
}
|
|
2031
|
+
return await res.json();
|
|
2032
|
+
}
|
|
2033
|
+
|
|
1132
2034
|
// src/radiant/core/patterns.ts
|
|
1133
2035
|
async function interpretPatterns(input) {
|
|
1134
2036
|
const prompt = buildInterpretationPrompt(input);
|
|
@@ -1146,15 +2048,17 @@ function buildInterpretationPrompt(input) {
|
|
|
1146
2048
|
const eventSample = formatEventSample(input.events, 30);
|
|
1147
2049
|
const canonicalList = (input.canonicalPatterns ?? []).length > 0 ? `Patterns the organization has already named (use these names if you see them):
|
|
1148
2050
|
${input.canonicalPatterns.map((p) => `- ${p}`).join("\n")}` : "No patterns have been named yet. Everything you observe is new.";
|
|
2051
|
+
const compressedWorld = compressWorldmodel(input.worldmodelContent);
|
|
2052
|
+
const cl = compressLens(input.lens);
|
|
1149
2053
|
const frame = input.lens.primary_frame;
|
|
1150
2054
|
const evalQuestions = frame.evaluation_questions.map((q, i) => `${i + 1}. ${q}`).join("\n");
|
|
1151
|
-
const forbiddenList =
|
|
1152
|
-
const jargonTable =
|
|
2055
|
+
const forbiddenList = cl.forbiddenPhrases;
|
|
2056
|
+
const jargonTable = cl.jargonTranslations;
|
|
1153
2057
|
return `You are a behavioral intelligence system reading team activity and producing a read for the reader who needs to act on it.
|
|
1154
2058
|
|
|
1155
|
-
##
|
|
2059
|
+
## Worldmodel (compressed)
|
|
1156
2060
|
|
|
1157
|
-
${
|
|
2061
|
+
${compressedWorld}
|
|
1158
2062
|
|
|
1159
2063
|
## What happened this window
|
|
1160
2064
|
|
|
@@ -1200,6 +2104,14 @@ ${jargonTable}
|
|
|
1200
2104
|
|
|
1201
2105
|
For example: don't say "update the worldmodel." Say "add a line to your strategy file."
|
|
1202
2106
|
|
|
2107
|
+
## When the same invariant keeps firing
|
|
2108
|
+
|
|
2109
|
+
If the prior read history or the current evidence shows the same worldmodel invariant being triggered repeatedly (by the same side \u2014 human or AI), name it in MEANING and ask the real question:
|
|
2110
|
+
|
|
2111
|
+
"This invariant has been tested N times across M reads. Always on the [human/AI] side. Either the team needs alignment on WHY this rule exists \u2014 or the team is telling you something the worldmodel hasn't absorbed yet."
|
|
2112
|
+
|
|
2113
|
+
Don't just say "invariant held." Say what it means that people keep pushing against the same wall.
|
|
2114
|
+
|
|
1203
2115
|
## Health is a valid read
|
|
1204
2116
|
|
|
1205
2117
|
If the activity is healthy and aligned with the worldmodel, SAY SO. Don't fabricate problems. Over-prescription is a voice failure. Legitimate outputs include:
|
|
@@ -1337,30 +2249,16 @@ Window: last ${input.windowDays} days \xB7 ${input.eventCount} events
|
|
|
1337
2249
|
Lens: ${input.lens.name}`
|
|
1338
2250
|
);
|
|
1339
2251
|
if (input.patterns.length > 0) {
|
|
1340
|
-
const canonical = input.patterns.filter((p) => p.type === "canonical");
|
|
1341
|
-
const candidates = input.patterns.filter((p) => p.type === "candidate");
|
|
1342
2252
|
let emergentBlock = "EMERGENT\n";
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
emergentBlock += `
|
|
2253
|
+
for (const p of input.patterns) {
|
|
2254
|
+
emergentBlock += `
|
|
1346
2255
|
${p.name}
|
|
1347
2256
|
`;
|
|
1348
|
-
|
|
1349
|
-
`;
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
if (candidates.length > 0) {
|
|
1353
|
-
emergentBlock += "\n Emergent (candidates \u2014 not yet in worldmodel)\n";
|
|
1354
|
-
for (const p of candidates) {
|
|
1355
|
-
emergentBlock += `
|
|
1356
|
-
${p.name} (candidate)
|
|
1357
|
-
`;
|
|
1358
|
-
emergentBlock += ` ${p.description}
|
|
2257
|
+
emergentBlock += ` ${p.description}
|
|
1359
2258
|
`;
|
|
1360
|
-
|
|
1361
|
-
|
|
2259
|
+
if (p.evidence.cited_invariant) {
|
|
2260
|
+
emergentBlock += ` Cited invariant: ${p.evidence.cited_invariant}
|
|
1362
2261
|
`;
|
|
1363
|
-
}
|
|
1364
2262
|
}
|
|
1365
2263
|
}
|
|
1366
2264
|
sections.push(emergentBlock.trimEnd());
|
|
@@ -1568,6 +2466,75 @@ function serializeYAML(obj, indent = 0) {
|
|
|
1568
2466
|
`;
|
|
1569
2467
|
}
|
|
1570
2468
|
|
|
2469
|
+
// src/radiant/core/discovery.ts
|
|
2470
|
+
var import_fs2 = require("fs");
|
|
2471
|
+
var import_path2 = require("path");
|
|
2472
|
+
var import_os = require("os");
|
|
2473
|
+
function discoverWorlds(options) {
|
|
2474
|
+
const worlds = [];
|
|
2475
|
+
const userDir = options?.userWorldsDir ?? (0, import_path2.join)((0, import_os.homedir)(), ".neuroverse", "worlds");
|
|
2476
|
+
if ((0, import_fs2.existsSync)(userDir)) {
|
|
2477
|
+
worlds.push(...loadWorldsFromDir(userDir, "user"));
|
|
2478
|
+
}
|
|
2479
|
+
if (options?.explicitWorldsDir) {
|
|
2480
|
+
worlds.push(...loadWorldsFromDir(options.explicitWorldsDir, "repo"));
|
|
2481
|
+
} else if (options?.repoDir) {
|
|
2482
|
+
const repoPaths = [
|
|
2483
|
+
(0, import_path2.join)(options.repoDir, "worlds"),
|
|
2484
|
+
(0, import_path2.join)(options.repoDir, ".neuroverse", "worlds")
|
|
2485
|
+
];
|
|
2486
|
+
for (const p of repoPaths) {
|
|
2487
|
+
if ((0, import_fs2.existsSync)(p)) {
|
|
2488
|
+
worlds.push(...loadWorldsFromDir(p, "repo"));
|
|
2489
|
+
break;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
const combinedContent = worlds.map((w) => `<!-- world: ${w.name} (${w.source}) -->
|
|
2494
|
+
${w.content}`).join("\n\n---\n\n");
|
|
2495
|
+
const summary = worlds.length === 0 ? "no worlds discovered" : worlds.map((w) => `${w.name} (${w.source})`).join(", ");
|
|
2496
|
+
return { worlds, combinedContent, summary };
|
|
2497
|
+
}
|
|
2498
|
+
function formatActiveWorlds(stack) {
|
|
2499
|
+
if (stack.worlds.length === 0) return "No worlds loaded.";
|
|
2500
|
+
const lines = ["ACTIVE WORLDS", ""];
|
|
2501
|
+
for (const w of stack.worlds) {
|
|
2502
|
+
const sourceLabel = w.source === "base" ? "universal" : w.source === "user" ? "personal" : "this repo";
|
|
2503
|
+
lines.push(` ${w.name} (${sourceLabel})`);
|
|
2504
|
+
}
|
|
2505
|
+
return lines.join("\n");
|
|
2506
|
+
}
|
|
2507
|
+
function loadWorldsFromDir(dirPath, source) {
|
|
2508
|
+
const dir = (0, import_path2.resolve)(dirPath);
|
|
2509
|
+
if (!(0, import_fs2.existsSync)(dir)) return [];
|
|
2510
|
+
const stat = (0, import_fs2.statSync)(dir);
|
|
2511
|
+
if (stat.isFile() && dir.endsWith(".md")) {
|
|
2512
|
+
try {
|
|
2513
|
+
return [{
|
|
2514
|
+
name: (0, import_path2.basename)(dir).replace(/\.worldmodel\.md$/, "").replace(/\.nv-world\.md$/, ""),
|
|
2515
|
+
source,
|
|
2516
|
+
path: dir,
|
|
2517
|
+
content: (0, import_fs2.readFileSync)(dir, "utf-8")
|
|
2518
|
+
}];
|
|
2519
|
+
} catch {
|
|
2520
|
+
return [];
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (!stat.isDirectory()) return [];
|
|
2524
|
+
const files = (0, import_fs2.readdirSync)(dir).filter(
|
|
2525
|
+
(f) => f.endsWith(".worldmodel.md") || f.endsWith(".nv-world.md")
|
|
2526
|
+
).sort();
|
|
2527
|
+
return files.map((f) => {
|
|
2528
|
+
const fullPath = (0, import_path2.join)(dir, f);
|
|
2529
|
+
return {
|
|
2530
|
+
name: f.replace(/\.worldmodel\.md$/, "").replace(/\.nv-world\.md$/, ""),
|
|
2531
|
+
source,
|
|
2532
|
+
path: fullPath,
|
|
2533
|
+
content: (0, import_fs2.readFileSync)(fullPath, "utf-8")
|
|
2534
|
+
};
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
|
|
1571
2538
|
// src/engine/text-utils.ts
|
|
1572
2539
|
function normalizeEventText(event) {
|
|
1573
2540
|
return [
|
|
@@ -2654,10 +3621,10 @@ function verdictToEvent(status, intent) {
|
|
|
2654
3621
|
// src/loader/world-loader.ts
|
|
2655
3622
|
async function loadWorldFromDirectory(dirPath) {
|
|
2656
3623
|
const { readFile } = await import("fs/promises");
|
|
2657
|
-
const { join:
|
|
2658
|
-
const { readdirSync:
|
|
3624
|
+
const { join: join4 } = await import("path");
|
|
3625
|
+
const { readdirSync: readdirSync4 } = await import("fs");
|
|
2659
3626
|
async function readJson(filename) {
|
|
2660
|
-
const filePath =
|
|
3627
|
+
const filePath = join4(dirPath, filename);
|
|
2661
3628
|
try {
|
|
2662
3629
|
const content = await readFile(filePath, "utf-8");
|
|
2663
3630
|
return JSON.parse(content);
|
|
@@ -2687,11 +3654,11 @@ async function loadWorldFromDirectory(dirPath) {
|
|
|
2687
3654
|
const metadataJson = await readJson("metadata.json");
|
|
2688
3655
|
const rules = [];
|
|
2689
3656
|
try {
|
|
2690
|
-
const rulesDir =
|
|
2691
|
-
const ruleFiles =
|
|
3657
|
+
const rulesDir = join4(dirPath, "rules");
|
|
3658
|
+
const ruleFiles = readdirSync4(rulesDir).filter((f) => f.endsWith(".json")).sort();
|
|
2692
3659
|
for (const file of ruleFiles) {
|
|
2693
3660
|
try {
|
|
2694
|
-
const content = await readFile(
|
|
3661
|
+
const content = await readFile(join4(rulesDir, file), "utf-8");
|
|
2695
3662
|
rules.push(JSON.parse(content));
|
|
2696
3663
|
} catch (err) {
|
|
2697
3664
|
process.stderr.write(
|
|
@@ -2851,25 +3818,25 @@ function emptyAudit(total, reason) {
|
|
|
2851
3818
|
}
|
|
2852
3819
|
|
|
2853
3820
|
// src/radiant/memory/palace.ts
|
|
2854
|
-
var
|
|
2855
|
-
var
|
|
3821
|
+
var import_fs3 = require("fs");
|
|
3822
|
+
var import_path3 = require("path");
|
|
2856
3823
|
function writeRead(exocortexDir, frontmatter, text) {
|
|
2857
|
-
const dir = (0,
|
|
2858
|
-
(0,
|
|
3824
|
+
const dir = (0, import_path3.resolve)(exocortexDir, "radiant", "reads");
|
|
3825
|
+
(0, import_fs3.mkdirSync)(dir, { recursive: true });
|
|
2859
3826
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2860
3827
|
const filename = `${date}.md`;
|
|
2861
|
-
const filepath = (0,
|
|
3828
|
+
const filepath = (0, import_path3.join)(dir, filename);
|
|
2862
3829
|
const content = `${frontmatter}
|
|
2863
3830
|
|
|
2864
3831
|
${text}
|
|
2865
3832
|
`;
|
|
2866
|
-
(0,
|
|
3833
|
+
(0, import_fs3.writeFileSync)(filepath, content, "utf-8");
|
|
2867
3834
|
return filepath;
|
|
2868
3835
|
}
|
|
2869
3836
|
function updateKnowledge(exocortexDir, persistence, options) {
|
|
2870
|
-
const dir = (0,
|
|
2871
|
-
(0,
|
|
2872
|
-
const filepath = (0,
|
|
3837
|
+
const dir = (0, import_path3.resolve)(exocortexDir, "radiant");
|
|
3838
|
+
(0, import_fs3.mkdirSync)(dir, { recursive: true });
|
|
3839
|
+
const filepath = (0, import_path3.join)(dir, "knowledge.md");
|
|
2873
3840
|
const totalReads = options?.totalReads ?? 0;
|
|
2874
3841
|
const existingUntriggered = loadUntriggeredCounts(filepath);
|
|
2875
3842
|
const lines = [
|
|
@@ -2956,14 +3923,14 @@ function updateKnowledge(exocortexDir, persistence, options) {
|
|
|
2956
3923
|
lines.push(`${name}=${count}`);
|
|
2957
3924
|
}
|
|
2958
3925
|
lines.push("-->");
|
|
2959
|
-
(0,
|
|
3926
|
+
(0, import_fs3.writeFileSync)(filepath, lines.join("\n"), "utf-8");
|
|
2960
3927
|
return filepath;
|
|
2961
3928
|
}
|
|
2962
3929
|
function loadUntriggeredCounts(filepath) {
|
|
2963
3930
|
const counts = /* @__PURE__ */ new Map();
|
|
2964
|
-
if (!(0,
|
|
3931
|
+
if (!(0, import_fs3.existsSync)(filepath)) return counts;
|
|
2965
3932
|
try {
|
|
2966
|
-
const content = (0,
|
|
3933
|
+
const content = (0, import_fs3.readFileSync)(filepath, "utf-8");
|
|
2967
3934
|
const match = content.match(
|
|
2968
3935
|
/<!-- untriggered_counts[\s\S]*?-->/
|
|
2969
3936
|
);
|
|
@@ -2981,13 +3948,13 @@ function loadUntriggeredCounts(filepath) {
|
|
|
2981
3948
|
return counts;
|
|
2982
3949
|
}
|
|
2983
3950
|
function loadPriorReads(exocortexDir) {
|
|
2984
|
-
const dir = (0,
|
|
2985
|
-
if (!(0,
|
|
2986
|
-
const files = (0,
|
|
3951
|
+
const dir = (0, import_path3.resolve)(exocortexDir, "radiant", "reads");
|
|
3952
|
+
if (!(0, import_fs3.existsSync)(dir)) return [];
|
|
3953
|
+
const files = (0, import_fs3.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
2987
3954
|
const reads = [];
|
|
2988
3955
|
for (const filename of files) {
|
|
2989
|
-
const filepath = (0,
|
|
2990
|
-
const content = (0,
|
|
3956
|
+
const filepath = (0, import_path3.join)(dir, filename);
|
|
3957
|
+
const content = (0, import_fs3.readFileSync)(filepath, "utf-8");
|
|
2991
3958
|
const date = filename.replace(".md", "");
|
|
2992
3959
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2993
3960
|
const frontmatter = fmMatch ? fmMatch[1] : "";
|
|
@@ -3078,21 +4045,80 @@ function resolveLens(id) {
|
|
|
3078
4045
|
async function emergent(input) {
|
|
3079
4046
|
const lens = resolveLens2(input.lensId);
|
|
3080
4047
|
const windowDays = input.windowDays ?? 14;
|
|
4048
|
+
let worldStack;
|
|
4049
|
+
let worldmodelContent = input.worldmodelContent;
|
|
4050
|
+
if (!worldmodelContent || worldmodelContent.trim() === "") {
|
|
4051
|
+
worldStack = discoverWorlds({ explicitWorldsDir: input.worldPath });
|
|
4052
|
+
worldmodelContent = worldStack.combinedContent;
|
|
4053
|
+
}
|
|
3081
4054
|
let statedIntent;
|
|
3082
4055
|
let exocortexContext;
|
|
3083
4056
|
let priorReadContext = "";
|
|
3084
4057
|
if (input.exocortexPath) {
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
4058
|
+
const repoName = input.scope.type === "repo" ? input.scope.repo : void 0;
|
|
4059
|
+
exocortexContext = readExocortex(input.exocortexPath, repoName);
|
|
4060
|
+
const compressed = compressExocortex(exocortexContext);
|
|
4061
|
+
if (compressed) {
|
|
4062
|
+
statedIntent = `## Stated Intent (from exocortex, compressed)
|
|
4063
|
+
|
|
4064
|
+
${compressed}
|
|
4065
|
+
|
|
4066
|
+
Compare stated intent against actual GitHub activity. Gaps = drift.`;
|
|
4067
|
+
}
|
|
3088
4068
|
const priorReads = loadPriorReads(input.exocortexPath);
|
|
3089
4069
|
if (priorReads.length > 0) {
|
|
3090
|
-
priorReadContext =
|
|
4070
|
+
priorReadContext = compressPriorReads(priorReads);
|
|
3091
4071
|
}
|
|
3092
4072
|
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
4073
|
+
let events;
|
|
4074
|
+
let orgRepos;
|
|
4075
|
+
if (input.scope.type === "org") {
|
|
4076
|
+
const orgResult = await fetchGitHubOrgActivity(
|
|
4077
|
+
input.scope,
|
|
4078
|
+
input.githubToken,
|
|
4079
|
+
{ windowDays }
|
|
4080
|
+
);
|
|
4081
|
+
events = orgResult.events;
|
|
4082
|
+
orgRepos = orgResult.repos;
|
|
4083
|
+
} else {
|
|
4084
|
+
events = await fetchGitHubActivity(input.scope, input.githubToken, {
|
|
4085
|
+
windowDays
|
|
4086
|
+
});
|
|
4087
|
+
}
|
|
4088
|
+
let adapterSignals = "";
|
|
4089
|
+
const activeAdapters = ["github"];
|
|
4090
|
+
const discordToken = process.env.DISCORD_TOKEN;
|
|
4091
|
+
const discordGuild = process.env.DISCORD_GUILD_ID;
|
|
4092
|
+
if (discordToken && discordGuild) {
|
|
4093
|
+
try {
|
|
4094
|
+
const discord = await fetchDiscordActivity(discordGuild, discordToken, { windowDays });
|
|
4095
|
+
events.push(...discord.events);
|
|
4096
|
+
adapterSignals += "\n\n" + formatDiscordSignalsForPrompt(discord.signals);
|
|
4097
|
+
activeAdapters.push("discord");
|
|
4098
|
+
} catch {
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
const slackToken = process.env.SLACK_TOKEN;
|
|
4102
|
+
if (slackToken) {
|
|
4103
|
+
try {
|
|
4104
|
+
const slack = await fetchSlackActivity(slackToken, { windowDays });
|
|
4105
|
+
events.push(...slack.events);
|
|
4106
|
+
adapterSignals += "\n\n" + formatSlackSignalsForPrompt(slack.signals);
|
|
4107
|
+
activeAdapters.push("slack");
|
|
4108
|
+
} catch {
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
const notionToken = process.env.NOTION_TOKEN;
|
|
4112
|
+
if (notionToken) {
|
|
4113
|
+
try {
|
|
4114
|
+
const notion = await fetchNotionActivity(notionToken, { windowDays });
|
|
4115
|
+
events.push(...notion.events);
|
|
4116
|
+
adapterSignals += "\n\n" + formatNotionSignalsForPrompt(notion.signals);
|
|
4117
|
+
activeAdapters.push("notion");
|
|
4118
|
+
} catch {
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
3096
4122
|
const classified = classifyEvents(events);
|
|
3097
4123
|
const signals = extractSignals(classified);
|
|
3098
4124
|
const scores = computeScores(signals, input.worldmodelContent !== "");
|
|
@@ -3103,7 +4129,7 @@ async function emergent(input) {
|
|
|
3103
4129
|
lens,
|
|
3104
4130
|
ai: input.ai,
|
|
3105
4131
|
canonicalPatterns: input.canonicalPatterns,
|
|
3106
|
-
statedIntent: statedIntent
|
|
4132
|
+
statedIntent: [statedIntent, adapterSignals, priorReadContext].filter(Boolean).join("\n\n") || void 0
|
|
3107
4133
|
});
|
|
3108
4134
|
const rewrittenPatterns = patterns.map((p) => lens.rewrite(p));
|
|
3109
4135
|
const allDescriptions = rewrittenPatterns.map((p) => p.description).join("\n");
|
|
@@ -3152,7 +4178,9 @@ async function emergent(input) {
|
|
|
3152
4178
|
voiceClean: voiceViolations.length === 0,
|
|
3153
4179
|
signals,
|
|
3154
4180
|
scores,
|
|
3155
|
-
eventCount: events.length
|
|
4181
|
+
eventCount: events.length,
|
|
4182
|
+
activeAdapters,
|
|
4183
|
+
worldStack
|
|
3156
4184
|
};
|
|
3157
4185
|
}
|
|
3158
4186
|
function computeScores(signals, worldmodelLoaded) {
|
|
@@ -3217,16 +4245,30 @@ var RADIANT_PACKAGE_VERSION = "0.0.0";
|
|
|
3217
4245
|
classifyActorDomain,
|
|
3218
4246
|
classifyEvents,
|
|
3219
4247
|
composeSystemPrompt,
|
|
4248
|
+
compressExocortex,
|
|
4249
|
+
compressLens,
|
|
4250
|
+
compressPriorReads,
|
|
4251
|
+
compressWorldmodel,
|
|
3220
4252
|
computePersistence,
|
|
3221
4253
|
createAnthropicAI,
|
|
3222
4254
|
createMockAI,
|
|
3223
4255
|
createMockGitHubAdapter,
|
|
4256
|
+
discoverWorlds,
|
|
3224
4257
|
emergent,
|
|
3225
4258
|
extractSignals,
|
|
4259
|
+
fetchDiscordActivity,
|
|
3226
4260
|
fetchGitHubActivity,
|
|
4261
|
+
fetchGitHubOrgActivity,
|
|
4262
|
+
fetchNotionActivity,
|
|
4263
|
+
fetchSlackActivity,
|
|
4264
|
+
formatActiveWorlds,
|
|
4265
|
+
formatDiscordSignalsForPrompt,
|
|
3227
4266
|
formatExocortexForPrompt,
|
|
4267
|
+
formatNotionSignalsForPrompt,
|
|
3228
4268
|
formatPriorReadsForPrompt,
|
|
3229
4269
|
formatScope,
|
|
4270
|
+
formatSlackSignalsForPrompt,
|
|
4271
|
+
formatTeamExocorticesForPrompt,
|
|
3230
4272
|
getLens,
|
|
3231
4273
|
interpretPatterns,
|
|
3232
4274
|
isPresent,
|
|
@@ -3235,13 +4277,16 @@ var RADIANT_PACKAGE_VERSION = "0.0.0";
|
|
|
3235
4277
|
listLenses,
|
|
3236
4278
|
loadPriorReads,
|
|
3237
4279
|
parseRepoScope,
|
|
4280
|
+
parseScope,
|
|
3238
4281
|
presenceAverage,
|
|
3239
4282
|
readExocortex,
|
|
4283
|
+
readTeamExocortices,
|
|
3240
4284
|
render,
|
|
3241
4285
|
scoreComposite,
|
|
3242
4286
|
scoreCyber,
|
|
3243
4287
|
scoreLife,
|
|
3244
4288
|
scoreNeuroVerse,
|
|
4289
|
+
sovereignConduitLens,
|
|
3245
4290
|
summarizeExocortex,
|
|
3246
4291
|
think,
|
|
3247
4292
|
updateKnowledge,
|